added energy, mult storage, button and profile | also fixed bugs
BIN
frontend/.DS_Store
vendored
|
@ -3,10 +3,8 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="minimum-scale=1, initial-scale=1, width=device-width"
|
||||
/>
|
||||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
|
|
Before Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 641 B |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 162 KiB |
BIN
frontend/src/.DS_Store
vendored
|
@ -32,6 +32,7 @@ function AppComponent() {
|
|||
<Route path='/referral' element={<RoutePage page='referral' />} />
|
||||
<Route path='/auction' element={<RoutePage page='auction' />} />
|
||||
<Route path='/styles' element={<RoutePage page='styles' />} />
|
||||
<Route path='*' element={<RoutePage page='main' />} />
|
||||
</Routes>
|
||||
<AuctionMainPopups />
|
||||
</Layout>
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
--grey22: #222222;
|
||||
--grey9A: #9A9A9A;
|
||||
--grey93: #939393;
|
||||
--grey1F: #1F1F1F;
|
||||
}
|
||||
|
||||
body {
|
||||
|
|
|
@ -8,14 +8,16 @@ import { useDispatch } from 'react-redux';
|
|||
import { updateCoinsRequestAsync } from '../../../store/me/actions';
|
||||
import axios from 'axios';
|
||||
import { DevPopup } from '../../Elements/DevPopup';
|
||||
import { saveMult } from '../../../store/mult';
|
||||
|
||||
interface IClickerBtn {
|
||||
coins: number,
|
||||
setCoins(a: number): void,
|
||||
energy: number
|
||||
energy: number,
|
||||
setMult(a: number): void
|
||||
}
|
||||
|
||||
export function ClickerBtn({ coins, setCoins, energy }: IClickerBtn) {
|
||||
export function ClickerBtn({ coins, setCoins, energy, setMult }: IClickerBtn) {
|
||||
const urlClick = useAppSelector<string>(state => state.urlClick);
|
||||
const token = useAppSelector<string>(state => state.token);
|
||||
const [fill, setFill] = useState(0);
|
||||
|
@ -27,13 +29,19 @@ export function ClickerBtn({ coins, setCoins, energy }: IClickerBtn) {
|
|||
const [gradient, setGradient] = useState(getGradient());
|
||||
let styleIndex = useAppSelector<number>(state => state.styleIndex);
|
||||
const [initEnergy, setEnergy] = useState(energy);
|
||||
const maxEnergy = Number(localStorage.getItem('eg'));
|
||||
//const maxEnergy = Number(localStorage.getItem('eg'));
|
||||
const [maxEnergy, setMaxEnergy] = useState(500);
|
||||
const [closeError, setCloseError] = useState(true);
|
||||
const [error, setError] = useState(false);
|
||||
const [animClose, setAnimClose] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
const savedEnergy = sessionStorage.getItem('eg');
|
||||
if(savedEnergy) {
|
||||
const encodeEnergy = atob(savedEnergy);
|
||||
setMaxEnergy(Number(encodeEnergy));
|
||||
}
|
||||
setFill((maxEnergy - initEnergy) / maxEnergy * 100);
|
||||
}, []);
|
||||
|
||||
|
@ -42,54 +50,36 @@ export function ClickerBtn({ coins, setCoins, energy }: IClickerBtn) {
|
|||
}, [styleIndex]);
|
||||
|
||||
const btnClick = () => {
|
||||
if (!(initEnergy === 0)) {
|
||||
sendClick();
|
||||
/*if(!error) {
|
||||
sendClick();
|
||||
const newEnergy = initEnergy - 1;
|
||||
const newFill = (maxEnergy - newEnergy) / maxEnergy * 100;
|
||||
if (newFill <= 100) {
|
||||
sendClick();
|
||||
const newCoins = coins + 1;
|
||||
dispatch<any>(updateCoinsRequestAsync(newCoins, 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);
|
||||
}
|
||||
} else {
|
||||
sendClick();
|
||||
}*/
|
||||
};
|
||||
|
||||
const sendClick = () => {
|
||||
if(urlClick && token) {
|
||||
axios.get(`${urlClick}/api/v1/click`, {
|
||||
if(token) {
|
||||
axios.post(`${urlClick}/api/v1/click/`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
//"Content-type": "application/json",
|
||||
"Authorization": `TelegramToken ${token}`
|
||||
}
|
||||
},
|
||||
}
|
||||
).then((resp) => {
|
||||
console.log(resp);
|
||||
//console.log(resp);
|
||||
if(resp.data) {
|
||||
const click = Number(resp.data.click.value);
|
||||
//
|
||||
const newEnergy = initEnergy - click;
|
||||
const encodeMult = btoa(click.toString());
|
||||
sessionStorage.setItem('mt', encodeMult);
|
||||
//
|
||||
const newEnergy = Number(resp.data.energy);
|
||||
setMult(Number(click.toFixed(2)))
|
||||
dispatch<any>(saveMult(Number(click.toFixed(2))));
|
||||
const newFill = (maxEnergy - newEnergy) / maxEnergy * 100;
|
||||
if (newFill <= 100) {
|
||||
const newCoins = coins + click;
|
||||
const newCoins = Number(coins + click);
|
||||
dispatch<any>(updateCoinsRequestAsync(newCoins, newEnergy))
|
||||
setCoins(newCoins);
|
||||
setEnergy(newEnergy)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
position: relative;
|
||||
width: 270px;
|
||||
height: 270px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ringContainer::before {
|
||||
|
@ -17,6 +18,7 @@
|
|||
}
|
||||
|
||||
.ringBig {
|
||||
user-select: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
@ -27,6 +29,7 @@
|
|||
}
|
||||
|
||||
.ringSmall {
|
||||
user-select: none;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
|
|
|
@ -13,7 +13,7 @@ export function ClickerBtnFooter({ text, className, onClick }: IClickerBtnFooter
|
|||
return (
|
||||
<div className={`${styles.container} ${className}`} onClick={onClick}>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.icon}><Icon icon={EIcons.BitCoinIcon} /></div>
|
||||
<div className={styles.icon}>{text === 'Аукцион' ? <Icon icon={EIcons.BitCoinIcon} /> :<Icon icon={EIcons.StyleIcon}/>}</div>
|
||||
<p style={ETextStyles.RwSb14120} className={styles.text}>{text}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -16,9 +16,9 @@ export function ClickerFooter() {
|
|||
<div className={styles.container}>
|
||||
<ClickerBtnFooter text='Стили' className={styles.btn} onClick={() => navigate('/styles')}/>
|
||||
<ClickerBtnFooter text='Аукцион' className={styles.btn} onClick={() => { !isDev ? navigate('/auction') : setCloseDev(false) }}/>
|
||||
<div className={styles.fire}>
|
||||
{ !isDev && <div className={styles.fire}>
|
||||
<Icon icon={EIcons.FireIcon}/>
|
||||
</div>
|
||||
</div>}
|
||||
{!closeDev && <ModalWindow removeBtn={true} setCloseAnimOut={setCloseAnimOut} closeAnimOut={closeAnimOut} setClose={setCloseDev} modalBlock={
|
||||
<DevPopup setClose={setCloseAnimOut} type='dev' />
|
||||
} />}
|
||||
|
|
47
frontend/src/shared/Clicker/PointsZoom/PointsZoom.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import styles from './pointszoom.module.css';
|
||||
import { formatNumber } from '../../../utils/formatNumber';
|
||||
import { ETextStyles } from '../../texts';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
interface IPointsZoom {
|
||||
points: number,
|
||||
setClose(a:boolean): void,
|
||||
className ?: string,
|
||||
closePointsAnim: boolean,
|
||||
setClosePointsAnim(a: boolean): void
|
||||
}
|
||||
|
||||
export function PointsZoom({ points, setClose, className, closePointsAnim, setClosePointsAnim }: IPointsZoom) {
|
||||
const [open, setOpen] = useState(true);
|
||||
const node = document.querySelector('#modal_root');
|
||||
if (!node) return null;
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setOpen(false);
|
||||
clearInterval(timer);
|
||||
}, 400);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (closePointsAnim) {
|
||||
const timer = setTimeout(() => {
|
||||
setClosePointsAnim(false);
|
||||
setClose(true);
|
||||
clearTimeout(timer);
|
||||
}, 400);
|
||||
}
|
||||
}, [closePointsAnim]);
|
||||
|
||||
return (
|
||||
<div className={`${styles.container} ${className} ${open ? styles.animBack : ''} ${closePointsAnim ? styles.animBackClose : ''}`}>
|
||||
{ReactDOM.createPortal((
|
||||
<div className={`${styles.innerContainer} ${open ? styles.animBlock : ''} ${closePointsAnim ? styles.animBlockClose : ''}`}>
|
||||
<div className={styles.icon} style={{ backgroundImage: `url('assets/btnIcon.png')` }}></div>
|
||||
<p className={styles.point} style={ETextStyles.InSb18100}>{formatNumber(Number(points.toFixed(2)))}</p>
|
||||
</div>
|
||||
), node)}
|
||||
</div>
|
||||
);
|
||||
}
|
1
frontend/src/shared/Clicker/PointsZoom/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './PointsZoom';
|
102
frontend/src/shared/Clicker/PointsZoom/pointszoom.module.css
Normal file
|
@ -0,0 +1,102 @@
|
|||
.container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(14, 14, 14, 0.01);
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
.innerContainer {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 10px;
|
||||
z-index: 50;
|
||||
width: calc(100% - 20px);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 5px 12px;
|
||||
border-radius: 20px;
|
||||
background-color: var(--grey1F);
|
||||
box-shadow: 0px 0px 30px 15px rgba(128, 135, 192, 0.25);
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.animBack {
|
||||
animation-name: animBack;
|
||||
animation-duration: 0.4s;
|
||||
animation-iteration-count: 1;
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
|
||||
.animBlock {
|
||||
animation-name: animBlock;
|
||||
animation-duration: 0.4s;
|
||||
animation-iteration-count: 1;
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
|
||||
@keyframes animBack {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animBlock {
|
||||
0% {
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
100% {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.animBackClose {
|
||||
animation-name: animBackClose;
|
||||
animation-duration: 0.4s;
|
||||
animation-iteration-count: 1;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
.animBlockClose {
|
||||
animation-name: animBlockClose;
|
||||
animation-duration: 0.4s;
|
||||
animation-iteration-count: 1;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
@keyframes animBackClose {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animBlockClose {
|
||||
0% {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
width: 64px;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ import styles from './profile.module.css';
|
|||
import { ETextStyles } from '../../texts';
|
||||
import { formatNumber } from '../../../utils/formatNumber';
|
||||
import { PersonIcon } from '../../Elements/PersonIcon';
|
||||
import { EIcons, Icon } from '../../Icons';
|
||||
|
||||
interface IProfileClicker {
|
||||
name: string,
|
||||
|
@ -14,12 +15,12 @@ interface IProfileClicker {
|
|||
export function Profile({ name, points, img, className }: IProfileClicker) {
|
||||
return (
|
||||
<div className={`${styles.container} ${className}`}>
|
||||
<PersonIcon img={img} size={30}/>
|
||||
{img ? <PersonIcon img={`${img}`} size={30}/> : <div className={styles.emptyIcon}><Icon icon={EIcons.ProfileIcon}/></div> }
|
||||
<div className={styles.content}>
|
||||
<p style={ETextStyles.RwSb12120} className={styles.name}>{name}</p>
|
||||
<div className={styles.pointsContainer}>
|
||||
<p className={styles.points} style={ETextStyles.InSb10120}>
|
||||
{formatNumber(points)}
|
||||
{formatNumber(Number(points.toFixed(2)))}
|
||||
</p>
|
||||
<div className={styles.icon} style={{ backgroundImage: "url('assets/btnIcon.png')"}}></div>
|
||||
</div>
|
||||
|
|
|
@ -38,3 +38,13 @@
|
|||
.content {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.emptyIcon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background-color: var(--grey12);
|
||||
border-radius: 50%;
|
||||
}
|
|
@ -10,7 +10,11 @@ import { UsersIcons } from '../../Elements/UsersIcons';
|
|||
import { formatNumber } from '../../../utils/formatNumber';
|
||||
import { DevPopup } from '../../Elements/DevPopup';
|
||||
|
||||
export function SectionsBlock() {
|
||||
interface ISectionsBlock {
|
||||
mult:number;
|
||||
}
|
||||
|
||||
export function SectionsBlock({ mult }: ISectionsBlock) {
|
||||
const scaleRef = 70;
|
||||
const [close, setClose] = useState(true);
|
||||
const navigate = useNavigate();
|
||||
|
@ -41,20 +45,23 @@ export function SectionsBlock() {
|
|||
<div className={styles.sectionContainer}>
|
||||
<div className={styles.leftContainer}>
|
||||
<CardSection title='Место в топе' onClick={() => {!isDev ? navigate('/rating') : setCloseDev(false)}}>
|
||||
{!isDev && <div className={styles.bottomRank}>
|
||||
{<div className={`${styles.bottomRank} ${isDev ? styles.dev : ''}`}>
|
||||
<div style={ETextStyles.InSb12120}>
|
||||
<span className={styles.rank1}>#</span>
|
||||
<span>{formatNumber(12980)}</span>
|
||||
<span>{formatNumber(1)}</span>
|
||||
</div>
|
||||
<UsersIcons size={16}/>
|
||||
</div>}
|
||||
</CardSection>
|
||||
<CardSection title='Множитель' onClick={() => { !isDev ? setClose(false) : setCloseDev(false) }}>
|
||||
{!isDev &&<PointsBlock points='1.50' />}
|
||||
<CardSection title='Множитель' onClick={() => { setClose(false) }}>
|
||||
<p style={ETextStyles.InSb12120}>
|
||||
<span style={{color: 'var(--primary)'}}>{'X '}</span>
|
||||
{mult}
|
||||
</p>
|
||||
</CardSection>
|
||||
</div>
|
||||
<CardSection title='Реферальное хранилище' className={styles.rigthEl} onClick={() => { !isDev ? navigate('/referral') : setCloseDev(false) }}>
|
||||
{!isDev &&<div>
|
||||
<CardSection title='Реферальное хранилище' className={styles.rigthEl} onClick={() => { navigate('/referral') }}>
|
||||
{<div className={isDev ? styles.dev : ''}>
|
||||
<PointsBlock points={formatNumber(800)} className={styles.scalePoints} />
|
||||
<div className={styles.scaleContainer}>
|
||||
<div className={styles.scale} style={{ width: `${scaleRef}px` }}></div>
|
||||
|
|
|
@ -55,3 +55,19 @@
|
|||
height: 10px;
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
.dev {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dev::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
left: -12px;
|
||||
width: calc(100% + 24px);
|
||||
height: calc(100% + 24px);
|
||||
border-radius: 14px;
|
||||
background: rgba(20, 20, 20, 0.80);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
|
@ -14,9 +14,9 @@ export function DevPopup({ setClose, type }: IDevPopup) {
|
|||
<div className={styles.iconContainer}>
|
||||
<div className={styles.icon} style={{backgroundImage: "url('assets/dev.png')"}}></div>
|
||||
</div>
|
||||
<h2 className={styles.title} style={ETextStyles.RwSb24100}>{type === 'dev' ? 'Скоро будет доступно' : 'Возникла ошибка'}</h2>
|
||||
<p className={styles.text} style={ETextStyles.RwSb14120}>{type === 'dev' ? 'Пока что делаем эту фичу. Скоро сможете поюзать.' : 'Мы пока не можем принимать клики, но скоро всё починим.'}</p>
|
||||
<Button text={type === 'dev' ? 'Продолжить кликать' : 'Принято'} stroke={true} onClick={() => setClose(true)}/>
|
||||
<h2 className={styles.title} style={ETextStyles.RwSb24100}>{type === 'dev' ? 'Скоро откроем' : 'Возникла ошибка'}</h2>
|
||||
<p className={styles.text} style={ETextStyles.RwSb14120}>{type === 'dev' ? <span>Подготавливаем что-то особенное для вас. Скоро увидимся на новом уровне!</span> : 'Мы пока не можем принимать клики, но скоро всё починим.'}</p>
|
||||
<Button text={type === 'dev' ? 'Продолжить кликать' : 'Принято'} onClick={() => setClose(true)}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ export const StylesSwiper: React.FC<IStylesSwiper> = memo(({ selectedStyle, setC
|
|||
transform: `rotate(${isActive ? 0 : deg}deg)`,
|
||||
filter: `blur(${isActive ? 0 : 3}px)`
|
||||
}}
|
||||
><div className={styles.img}></div></div>
|
||||
><div className={styles.img} style={{ backgroundImage: `url('assets/style${index + 1}.png')`}}></div></div>
|
||||
);
|
||||
}}
|
||||
</SwiperSlide>))}
|
||||
|
|
|
@ -24,33 +24,33 @@
|
|||
background: var(--gradientBlue);
|
||||
}
|
||||
|
||||
.card1 div {
|
||||
/*.card1 div {
|
||||
background-image: url('assets/style1.png');
|
||||
}
|
||||
}*/
|
||||
|
||||
.card2 {
|
||||
background: var(--gradientOrange);
|
||||
}
|
||||
|
||||
.card2 div {
|
||||
/*.card2 div {
|
||||
background-image: url('assets/style2.png');
|
||||
}
|
||||
}*/
|
||||
|
||||
.card3 {
|
||||
background: var(--gradientYellow);
|
||||
}
|
||||
|
||||
.card3 div {
|
||||
/*.card3 div {
|
||||
background-image: url('assets/style3.png');
|
||||
}
|
||||
}*/
|
||||
|
||||
.card4 {
|
||||
background: var(--gradientOrangeYellow);
|
||||
}
|
||||
|
||||
.card4 div {
|
||||
/*.card4 div {
|
||||
background-image: url('assets/style4.png');
|
||||
}
|
||||
}*/
|
||||
|
||||
.disabled {
|
||||
position: relative;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
position: absolute;
|
||||
top: 0;
|
||||
border-radius: 50%;
|
||||
background-color: var(--white);
|
||||
background-color: var(--grey6C);
|
||||
border: 1px solid var(--grey12);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import { ArrowIcon, BitCoinIcon, ChartIcon, CopyIcon, FireIcon, SmallCoinIcon, TrendIcon, UpPriceIcon } from './commonIcons';
|
||||
import { ArrowIcon, BitCoinIcon, ChartIcon, CopyIcon, FireIcon, ProfileIcon, SmallCoinIcon, StyleIcon, TrendIcon, UpPriceIcon } from './commonIcons';
|
||||
import { MedalFirstIcon, MedalSecondIcon, MedalThirdIcon } from './medals';
|
||||
interface IconsProps {
|
||||
icon: EIcons;
|
||||
|
@ -17,7 +17,9 @@ export const EIcons = {
|
|||
TrendIcon: <TrendIcon/>,
|
||||
ChartIcon: <ChartIcon/>,
|
||||
CopyIcon: <CopyIcon/>,
|
||||
UpPriceIcon: <UpPriceIcon/>
|
||||
UpPriceIcon: <UpPriceIcon/>,
|
||||
ProfileIcon: <ProfileIcon/>,
|
||||
StyleIcon: <StyleIcon />
|
||||
} as const;
|
||||
|
||||
type EIcons = typeof EIcons[keyof typeof EIcons];
|
||||
|
|
|
@ -77,3 +77,21 @@ export function UpPriceIcon() {
|
|||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function ProfileIcon() {
|
||||
return (
|
||||
<svg width="19" height="20" viewBox="0 0 19 20" fill="none" xmlns="http://www.w3.org/2000/svg" style={{ fill: 'var(--primary)' }}>
|
||||
<path d="M10.9808 10.8314C10.7302 10.8314 10.4853 10.7581 10.2769 10.6207C10.0686 10.4834 9.90615 10.2881 9.81026 10.0596C9.71436 9.83118 9.68927 9.5798 9.73816 9.33727C9.78704 9.09474 9.90772 8.87197 10.0849 8.69712C10.2621 8.52227 10.4879 8.40319 10.7336 8.35495C10.9794 8.30671 11.2342 8.33147 11.4657 8.4261C11.6972 8.52073 11.8951 8.68097 12.0343 8.88658C12.1735 9.09218 12.2478 9.33391 12.2478 9.58118C12.2478 9.91277 12.1144 10.2308 11.8767 10.4653C11.6391 10.6997 11.3169 10.8314 10.9808 10.8314ZM17.1901 6.8923C16.851 4.98836 15.8518 3.25993 14.3635 2.00289C12.8752 0.745842 10.9904 0.0383476 9.03172 0.00151426C7.07305 -0.035319 5.1623 0.6008 3.62656 1.80098C2.09083 3.00116 1.0256 4.69076 0.613237 6.58057L0 9.99794H1.26702C2.78369 9.98872 4.27933 9.64709 5.64584 8.99773C5.54579 9.17648 5.49238 9.37699 5.49041 9.58118C5.48927 9.82395 5.55977 10.0618 5.69331 10.2657C5.82685 10.4696 6.01766 10.6307 6.24244 10.7293C6.46722 10.828 6.71625 10.86 6.95913 10.8213C7.20201 10.7827 7.42824 10.6751 7.61019 10.5117C7.79215 10.3483 7.92196 10.1362 7.98378 9.9012C8.0456 9.66622 8.03676 9.41857 7.95834 9.18847C7.87991 8.95837 7.7353 8.75578 7.54215 8.60542C7.34899 8.45507 7.11566 8.36345 6.87062 8.34176C8.6728 7.23222 10.1991 5.73698 11.3364 3.96669C10.839 3.69422 10.2984 3.50708 9.73746 3.41324C8.43834 5.3128 5.67371 7.96668 2.02132 8.29675L2.2705 6.90563C2.61543 5.36113 3.50548 3.98789 4.78171 3.03112C6.05794 2.07436 7.63726 1.59638 9.23764 1.68252C10.838 1.76866 12.3553 2.41333 13.5185 3.50139C14.6817 4.58946 15.4151 6.05008 15.5877 7.62245L15.6536 8.24341L16.277 8.35426C16.5847 8.41366 16.8592 8.58356 17.0474 8.8311C17.2355 9.07864 17.324 9.38623 17.2956 9.69439C17.2672 10.0026 17.124 10.2894 16.8938 10.4994C16.6635 10.7094 16.3625 10.8277 16.0489 10.8314C15.9763 10.8314 15.1307 10.6856 15.1307 10.6856L14.8925 11.3524C14.1188 13.5162 11.513 15.8325 8.86913 15.8325C6.31313 15.8325 3.78163 13.6495 2.92175 11.5374C2.37403 11.6194 1.821 11.6621 1.26702 11.6649H1.16566C1.21803 11.8158 1.25519 11.915 1.26702 11.9475C2.06991 13.948 3.56664 15.6022 5.49041 16.6151V20H7.17977V17.2761C7.73095 17.4213 8.29867 17.4963 8.86913 17.4995V17.4995C9.43943 17.4971 10.0071 17.4232 10.5585 17.2794V20H12.2478V16.6276C13.9914 15.7027 15.3913 14.2542 16.2449 12.4918C16.9086 12.4471 17.5377 12.1828 18.0304 11.7416C18.523 11.3004 18.8505 10.7082 18.9598 10.0606C19.0691 9.41303 18.9539 8.748 18.6327 8.17306C18.3116 7.59812 17.8033 7.14688 17.1901 6.8923V6.8923Z" style={{ fill: 'var(--primary)' }} />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function StyleIcon() {
|
||||
return (
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.6663 5.20659V9.26658L13.5797 8.33325C13.0597 7.88658 12.2197 7.88658 11.6997 8.33325L8.92634 10.7133C8.40634 11.1599 7.56634 11.1599 7.04634 10.7133L6.81967 10.5266C6.34634 10.1133 5.59301 10.0733 5.05967 10.4333L1.77967 12.6333L1.70634 12.6866C1.45967 12.1533 1.33301 11.5199 1.33301 10.7933V5.20659C1.33301 2.77992 2.77967 1.33325 5.20634 1.33325H10.793C13.2197 1.33325 14.6663 2.77992 14.6663 5.20659Z" fill="#343434" />
|
||||
<path d="M5.99975 6.91992C6.87604 6.91992 7.58642 6.20954 7.58642 5.33325C7.58642 4.45696 6.87604 3.74658 5.99975 3.74658C5.12346 3.74658 4.41309 4.45696 4.41309 5.33325C4.41309 6.20954 5.12346 6.91992 5.99975 6.91992Z" fill="white" />
|
||||
<path d="M14.667 9.26662V10.7933C14.667 13.22 13.2204 14.6666 10.7937 14.6666H5.20703C3.50703 14.6666 2.28036 13.9533 1.70703 12.6866L1.78036 12.6333L5.06036 10.4333C5.5937 10.0733 6.34703 10.1133 6.82036 10.5266L7.04703 10.7133C7.56703 11.16 8.40703 11.16 8.92703 10.7133L11.7004 8.33329C12.2204 7.88662 13.0604 7.88662 13.5804 8.33329L14.667 9.26662Z" style={{ fill: 'var(--primary)' }} />
|
||||
</svg>
|
||||
)
|
||||
}
|
|
@ -15,11 +15,21 @@ interface IModalWindow {
|
|||
export function ModalWindow({ modalBlock, setClose, removeBtn, closeAnimOut, setCloseAnimOut }: IModalWindow) {
|
||||
const node = document.querySelector('#modal_root');
|
||||
const [closeAnim, setCloseAnim] = useState(false);
|
||||
const html = document.querySelector('html');
|
||||
|
||||
useEffect(() => {
|
||||
if (html) {
|
||||
html.style.overflowY = 'hidden';
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!node) return null;
|
||||
|
||||
const closePopUp = () => {
|
||||
setCloseAnim(true);
|
||||
if (html) {
|
||||
html.style.overflowY = 'auto';
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
setClose(true);
|
||||
|
|
|
@ -6,6 +6,10 @@ import { ETextStyles } from '../../texts';
|
|||
import { SectionsBlock } from '../../Clicker/SectionsBlock';
|
||||
import { ClickerFooter } from '../../Clicker/ClickerFooter';
|
||||
import { StyleElements } from '../../Clicker/StyleElements';
|
||||
import { PointsZoom } from '../../Clicker/PointsZoom';
|
||||
import { Timer } from '../../Auction/Timer';
|
||||
import { useWindowSize } from 'usehooks-ts';
|
||||
import { useAppSelector } from '../../hooks/useAppSelector';
|
||||
|
||||
interface IClickerPageInterface {
|
||||
name: string,
|
||||
|
@ -17,16 +21,42 @@ interface IClickerPageInterface {
|
|||
export function ClickerPage({ name, points, img, energy }: IClickerPageInterface) {
|
||||
const styleIndex = Number(localStorage.getItem('selectedStyle'));
|
||||
const [coins, setCoins] = useState(points);
|
||||
const [mult, setMult] = useState(1);
|
||||
const [closePoints, setClosePoints] = useState(true);
|
||||
const [closePointsAnim, setClosePointsAnim] = useState(false);
|
||||
const { width, height } = useWindowSize();
|
||||
const savedMult = useAppSelector<number>(state => state.mult);
|
||||
|
||||
useEffect(() => {
|
||||
setMult(savedMult);
|
||||
}, [savedMult]);
|
||||
|
||||
useEffect(() => {
|
||||
//@ts-ignore
|
||||
let timer;
|
||||
|
||||
if (points !== coins) {
|
||||
setClosePoints(false);
|
||||
timer = setTimeout(() => {
|
||||
setClosePointsAnim(true);
|
||||
}, 2000);
|
||||
}
|
||||
return () => {
|
||||
//@ts-ignore
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}, [coins]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.records}>
|
||||
{!closePoints && <PointsZoom points={coins} setClosePointsAnim={setClosePointsAnim} setClose={setClosePoints} className={styles.pointsAnim} closePointsAnim={closePointsAnim}/>}
|
||||
<Profile name={name} points={coins} className={styles.profile} img={img}/>
|
||||
<h1 style={ETextStyles.RwSb24100} className={styles.title}>Мои рекорды</h1>
|
||||
<SectionsBlock />
|
||||
<SectionsBlock mult={mult}/>
|
||||
</div>
|
||||
<div className={styles.clicker}>
|
||||
<ClickerBtn coins={coins} setCoins={setCoins} energy={energy}/>
|
||||
<div className={styles.clicker} style={{height: `${height > 670 && 'calc(100vh - 355px)'}`}}>
|
||||
<ClickerBtn coins={coins} setCoins={setCoins} energy={energy} setMult={setMult}/>
|
||||
</div>
|
||||
<ClickerFooter />
|
||||
{styleIndex != 0 && <div>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
.clicker {
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
margin-bottom: 24px;
|
||||
margin-bottom: 100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
@ -39,3 +39,10 @@
|
|||
.profile {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/*.pointsAnim {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}*/
|
|
@ -12,6 +12,7 @@ import { useUserData } from '../../hooks/useUserData';
|
|||
import { Spinner } from '../../Elements/Spinner';
|
||||
import { updateBackground } from '../../../utils/updateBackground';
|
||||
import { ErrorPage } from '../ErrorPage';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface IRoutePage {
|
||||
page: string
|
||||
|
@ -20,26 +21,37 @@ interface IRoutePage {
|
|||
export function RoutePage({ page }: IRoutePage) {
|
||||
const verified = useTgData();
|
||||
const { dataUser, loadingUser, errorUser } = useUserData();
|
||||
const navigate = useNavigate();
|
||||
//@ts-ignore
|
||||
const tg = window.Telegram.WebApp;
|
||||
var BackButton = tg.BackButton;
|
||||
|
||||
useEffect(() => {
|
||||
updateBackground(page);
|
||||
updateStyles();
|
||||
if(page === 'main') {
|
||||
BackButton.hide();
|
||||
} else {
|
||||
BackButton.show();
|
||||
}
|
||||
}, [page]);
|
||||
|
||||
//{!verified ? <WrongSourcePage/> :
|
||||
//}
|
||||
BackButton.onClick(function () {
|
||||
navigate(-1);
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
{page === 'main' && !loadingUser && !errorUser && dataUser.name && dataUser.avatar && <ClickerPage name={dataUser.name} points={Number(dataUser.points)} img={dataUser.avatar} energy={Number(dataUser.energy)}/>}
|
||||
{!verified ? <WrongSourcePage /> : <div>
|
||||
{ //@ts-ignore
|
||||
page === 'main' && !loadingUser && !errorUser && dataUser.name && <ClickerPage name={dataUser.name} points={Number(dataUser.points)} img={dataUser.avatar} energy={Number(dataUser.energy)}/>}
|
||||
{page === 'rating' && !loadingUser && !errorUser && <RatingPage />}
|
||||
{page === 'referral' && !loadingUser && !errorUser && <StoragePage />}
|
||||
{page === 'auction' && !loadingUser && !errorUser && <AuctionPage />}
|
||||
{page === 'styles' && !loadingUser && !errorUser && <StylesPage />}
|
||||
{(loadingUser) && <div className={styles.spinnerContainer}><Spinner color='#FFFFFF' size='50px' thickness='6px' className={styles.spinner} /></div> }
|
||||
{errorUser && !loadingUser && <ErrorPage detail={errorUser}/>}
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,30 +2,41 @@ import React, { useEffect, useState } from 'react';
|
|||
import styles from './storagepage.module.css';
|
||||
import { ETextStyles } from '../../texts';
|
||||
import { StorageBtn } from '../../Storage/StorageBtn';
|
||||
import { StorageScale } from '../../Storage/StorageScale';
|
||||
import { PopupCard } from '../../Elements/PopupCard';
|
||||
import { Button } from '../../Button';
|
||||
import { EIcons, Icon } from '../../Icons';
|
||||
import { сopyTextToClipboard } from '../../../utils/copyText';
|
||||
import { Notification } from '../../Notification';
|
||||
import { StoragePageBlock } from '../../Storage/StoragePageBlock';
|
||||
import { FriendsPageBlock } from '../../Storage/FriendsPageBlock';
|
||||
import { useAppSelector } from '../../hooks/useAppSelector';
|
||||
import { DevPopup } from '../../Elements/DevPopup';
|
||||
import { ModalWindow } from '../../ModalWindow';
|
||||
|
||||
export function StoragePage() {
|
||||
const userId = useAppSelector<string>(state => state.userTg.id);
|
||||
const [page, setPage] = useState('storage');
|
||||
const refLink = 'https://open.spotify.com/';
|
||||
const refLink = `https://t.me/sapphirecrown_bot?start=user_${userId}`;
|
||||
const [showNotif, setShow] = useState(false);
|
||||
const [closeAnimOut, setCloseAnimOut] = useState(false);
|
||||
const [closeDev, setCloseDev] = useState(true);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 style={ETextStyles.RwSb30100} className={styles.title}>Реферальная программа</h1>
|
||||
<div className={styles.btnGroup}>
|
||||
<StorageBtn active={page === 'storage'} type={'storage'} onClick={() => setPage('storage')}/>
|
||||
<StorageBtn active={page === 'friends'} type={'friends'} onClick={() => setPage('friends')} />
|
||||
<StorageBtn isDev={true} active={page === 'friends'} type={'friends'} onClick={() => {
|
||||
setCloseDev(false);
|
||||
//setPage('friends')
|
||||
}
|
||||
} />
|
||||
</div>
|
||||
{page === 'storage' ? <StoragePageBlock/> : <FriendsPageBlock/>}
|
||||
<Button className={styles.btn} icon={<Icon icon={EIcons.CopyIcon} />} text='Пригласить друга' onClick={() => { сopyTextToClipboard(refLink); setShow(true)}}/>
|
||||
{showNotif && <Notification title='Успешно' text='Пригласительная ссылка скопирована' setShow={setShow} />}
|
||||
{!closeDev && <ModalWindow removeBtn={true} setCloseAnimOut={setCloseAnimOut} closeAnimOut={closeAnimOut} setClose={setCloseDev} modalBlock={
|
||||
<DevPopup setClose={setCloseAnimOut} type='dev' />
|
||||
} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,8 +9,10 @@ export function WrongSourcePage() {
|
|||
|
||||
return (
|
||||
<div className={styles.container} style={{ height: `${height}px` }}>
|
||||
<div className={styles.innerContainer}>
|
||||
<h1 style={ETextStyles.RwSb24100} className={styles.title}>Похоже вы вошли не по той ссылке...</h1>
|
||||
<Button text='Войти через Telegram'/>
|
||||
<Button text='Войти через Telegram' onClick={() => document.location.href = 'https://t.me/sapphirecrown_bot'}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,15 @@
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.innerContainer {
|
||||
max-width: 400px;
|
||||
gap: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.title {
|
||||
|
|
|
@ -6,14 +6,15 @@ import { EIcons, Icon } from '../../Icons';
|
|||
interface IStorageBtn {
|
||||
type: 'storage' | 'friends',
|
||||
active: boolean,
|
||||
onClick(): void
|
||||
onClick(): void,
|
||||
isDev ?: boolean,
|
||||
}
|
||||
|
||||
export function StorageBtn({ type, active, onClick }: IStorageBtn) {
|
||||
export function StorageBtn({ type, active, onClick, isDev=false }: IStorageBtn) {
|
||||
const selectedStyle = Number(localStorage.getItem('selectedStyle'));
|
||||
|
||||
return (
|
||||
<button className={`${styles.container} ${active ? styles.fill : styles.stroke}`} style={ETextStyles.RwSb12120} onClick={onClick}>
|
||||
<button className={`${styles.container} ${active ? styles.fill : styles.stroke} ${isDev ? styles.dev : ''}`} style={ETextStyles.RwSb12120} onClick={onClick}>
|
||||
<div className={styles.content}>
|
||||
{type === 'storage' ? <Icon icon={EIcons.TrendIcon} /> : <Icon icon={EIcons.ChartIcon} />}
|
||||
<p className={selectedStyle === 2 ? styles.darkText : ''}>{type === 'storage' ? 'Хранилище' : 'Друзья'}</p>
|
||||
|
|
|
@ -22,5 +22,21 @@
|
|||
}
|
||||
|
||||
.stroke {
|
||||
border: 1px solid var(--primary);
|
||||
/*border: 1px solid var(--primary);*/
|
||||
border: 1px solid var(--grey6C);
|
||||
}
|
||||
|
||||
.dev {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dev::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgb(50, 50, 50, 0.5);
|
||||
border-radius: 30px;
|
||||
}
|
|
@ -1,22 +1,33 @@
|
|||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import styles from './storagepageblock.module.css';
|
||||
import { StorageScale } from '../StorageScale';
|
||||
import { ETextStyles } from '../../texts';
|
||||
import { PopupCard } from '../../Elements/PopupCard';
|
||||
import { ModalWindow } from '../../ModalWindow';
|
||||
import { DevPopup } from '../../Elements/DevPopup';
|
||||
|
||||
export function StoragePageBlock() {
|
||||
const [closeAnimOut, setCloseAnimOut] = useState(false);
|
||||
const [closeDev, setCloseDev] = useState(true);
|
||||
const isDev = true;
|
||||
return (
|
||||
<div>
|
||||
<h2 style={ETextStyles.RwSb18120} className={styles.title}>Хранилище</h2>
|
||||
<StorageScale points='400' percent={70} className={styles.scale} />
|
||||
<p style={ETextStyles.RwRg10120} className={styles.descr}>
|
||||
<div className={`${styles.containerStorage}`} onClick={() => setCloseDev(false)}>
|
||||
<StorageScale points='0' percent={0} className={styles.scale} isDev={true}/>
|
||||
{!isDev && <p style={ETextStyles.RwRg10120} className={styles.descr}>
|
||||
В хранилище приходит часть коинов, заработанная вашими друзьями. Считаем так: количество коинов * 5%. Хранилище пополняется каждый вечер.
|
||||
</p>
|
||||
</p>}
|
||||
{isDev && <div style={{height: '30px'}}></div> }
|
||||
</div>
|
||||
<h2 style={ETextStyles.RwSb18120} className={styles.title}>Как пригласить друга?</h2>
|
||||
<div className={styles.cards}>
|
||||
<PopupCard img='assets/Chain.png' title='Отправляй ссылку другу' text={<span>Друг присоединяется по пригласительной ссылке и становится рефералом, как только совершает активность в приложении.</span>} />
|
||||
<PopupCard img='assets/Money.png' title='Зарабатывайте вместе' text={<span>Друг кликает, ты получаешь 5% его кликов, а он 3% с твоих. Не забывай забирать коины из хранилища!</span>} />
|
||||
</div>
|
||||
{!closeDev && <ModalWindow removeBtn={true} setCloseAnimOut={setCloseAnimOut} closeAnimOut={closeAnimOut} setClose={setCloseDev} modalBlock={
|
||||
<DevPopup setClose={setCloseAnimOut} type='dev' />
|
||||
} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,3 +17,20 @@
|
|||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.containerStorage {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dev {
|
||||
z-index: 3;
|
||||
position: absolute;
|
||||
width: calc(100% + 10px);
|
||||
height: calc(100% + 10px);
|
||||
top: -2px;
|
||||
left: -5px;
|
||||
border-radius: 25px;
|
||||
background-color: rgb(37, 37, 37, 0.2);;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,10 @@ interface IStorageScale {
|
|||
percent: number,
|
||||
points: string,
|
||||
className ?: string,
|
||||
isDev?: boolean,
|
||||
}
|
||||
|
||||
export function StorageScale({ percent, points, className }: IStorageScale) {
|
||||
export function StorageScale({ percent, points, className, isDev=false }: IStorageScale) {
|
||||
const [showNotif, setShow] = useState(false);
|
||||
const [initpercent, setPercent] = useState(percent);
|
||||
|
||||
|
@ -26,8 +27,9 @@ export function StorageScale({ percent, points, className }: IStorageScale) {
|
|||
<div className={styles.content} style={ETextStyles.InSb16120}>
|
||||
{initpercent ===100 && <p>Забрать</p>}
|
||||
{initpercent > 0 && <PointsBlock points={points} sizeIcon={20} sizeText={16} />}
|
||||
{initpercent === 0 && <div className={styles.imgVolt} style={{backgroundImage: "url('assets/Volt.png')"}}></div>}
|
||||
{initpercent === 0 && <p style={ETextStyles.InRg14120}>Больше друзей — быстрее заполнение</p> }
|
||||
{initpercent === 0 && !isDev && <div className={styles.imgVolt} style={{backgroundImage: "url('assets/Volt.png')"}}></div>}
|
||||
{initpercent === 0 && !isDev && <p style={ETextStyles.InRg14120}>Больше друзей — быстрее заполнение</p> }
|
||||
{isDev && <p style={ETextStyles.InRg14120}>Скоро откроем</p>}
|
||||
</div>
|
||||
<div className={styles.scale} style={{ width: `${initpercent}%`}}></div>
|
||||
{showNotif && <Notification title='Пополнение' text={`Баланс баллов увеличен на ${points}`} setShow={setShow} />}
|
||||
|
|
23
frontend/src/shared/hooks/useReferral.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { useDispatch } from 'react-redux';
|
||||
import { useEffect } from 'react';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
import { RootState } from '../../store/reducer';
|
||||
import { Action } from 'redux';
|
||||
import { useAppSelector } from './useAppSelector';
|
||||
import { saveReferral } from '../../store/referral';
|
||||
|
||||
export function useReferral() {
|
||||
const dispatch = useDispatch<ThunkDispatch<RootState, unknown, Action>>();
|
||||
const savedReferral = useAppSelector<string>(state => state.token);
|
||||
|
||||
useEffect(() => {
|
||||
if (savedReferral.length === 0) {
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const referredBy = currentUrl.searchParams.get("referred_by");
|
||||
if (referredBy) {
|
||||
dispatch<any>(saveReferral(referredBy))
|
||||
}
|
||||
}
|
||||
|
||||
}, [savedReferral]);
|
||||
}
|
|
@ -17,8 +17,6 @@ export function useTgData() {
|
|||
if (savedToken.length === 0) {
|
||||
//@ts-ignore
|
||||
const [user, token]: [IUserTg, string] = verificationTg();
|
||||
console.log(`user из useTgData ${user}`);
|
||||
console.log(`token3 ${token}`);
|
||||
if (token.length != 0 && user.id && user.id.length != 0) {
|
||||
setVerified(true);
|
||||
dispatch<any>(saveToken(token));
|
||||
|
|
|
@ -3,20 +3,29 @@ import { IUserData, meRequestAsync } from "../../store/me/actions";
|
|||
import { useEffect } from 'react';
|
||||
import { useAppSelector } from './useAppSelector';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { saveReferral } from '../../store/referral';
|
||||
|
||||
export function useUserData() {
|
||||
const dataUser = useAppSelector<IUserData>(state => state.me.data);
|
||||
const loadingUser = useAppSelector<boolean>(state => state.me.loading);
|
||||
const errorUser = useAppSelector<String>(state => state.me.error);
|
||||
const token = useAppSelector<string>(state => state.token);
|
||||
const savedReferral = useAppSelector<string>(state => state.token);
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
//if (!token) navigate('/auth/welcome');
|
||||
if (savedReferral.length === 0) {
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const referredBy = currentUrl.searchParams.get("referred_by");
|
||||
if (referredBy) {
|
||||
dispatch<any>(saveReferral(referredBy))
|
||||
}
|
||||
}
|
||||
if (dataUser.username?.length != 0) {
|
||||
//@ts-ignore
|
||||
dispatch(meRequestAsync());
|
||||
|
||||
}
|
||||
}, [token]);
|
||||
|
||||
return { dataUser, loadingUser, errorUser };
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Action, ActionCreator } from "redux";
|
|||
import { ThunkAction } from "redux-thunk";
|
||||
import { RootState } from "../reducer";
|
||||
import axios from "axios";
|
||||
import { saveMult } from "../mult";
|
||||
|
||||
export interface IUserData {
|
||||
tgId?: number;
|
||||
|
@ -54,10 +55,33 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
|
|||
const token = getState().token;
|
||||
const URL = getState().url;
|
||||
const URLClick = getState().urlClick;
|
||||
//localStorage.setItem('eg', '500');
|
||||
/*if (tgId && URL && !meData.avatar && token.length != 0 && URLClick) {
|
||||
const referral = getState().referral;
|
||||
|
||||
const firstClick = (token: string) => {
|
||||
axios.post(`${URLClick}/api/v1/click/`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `TelegramToken ${token}`
|
||||
}
|
||||
}
|
||||
).then(resp => {
|
||||
const click = Number(resp.data.click.value);
|
||||
dispatch<any>(saveMult(click));
|
||||
const clickCode = btoa(click.toString());
|
||||
sessionStorage.setItem('mt', clickCode);
|
||||
});
|
||||
};
|
||||
|
||||
if (tgId && !meData.username && token.length != 0) {
|
||||
dispatch(meRequest());
|
||||
axios.get(`${URL}/api/v1/users/${tgId}/`, {
|
||||
let urlUser = '';
|
||||
if (referral.length != 0) {
|
||||
urlUser = `${URL}/api/v1/users/${tgId}?referred_by=${referral}`;
|
||||
} else {
|
||||
urlUser = `${URL}/api/v1/users/${tgId}/`;
|
||||
}
|
||||
axios.get(urlUser, {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
"Authorization": `TelegramToken ${token}`
|
||||
|
@ -65,8 +89,6 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
|
|||
},
|
||||
).then((resp) => {
|
||||
const user = resp.data;
|
||||
const encodeToken = btoa(unescape(encodeURIComponent(token)));
|
||||
const savedToken = localStorage.getItem('sts');
|
||||
axios.get(`${URLClick}/api/v1/energy`, {
|
||||
headers: {
|
||||
//"Content-type": "application/json",
|
||||
|
@ -75,14 +97,34 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
|
|||
},
|
||||
).then((resp) => {
|
||||
const energy = resp.data.energy;
|
||||
//
|
||||
const encodeToken = btoa(unescape(encodeURIComponent(token)));
|
||||
const savedToken = sessionStorage.getItem('tk');
|
||||
if (savedToken) {
|
||||
if (savedToken != encodeToken) {
|
||||
localStorage.setItem('eg', '200'); //enegry
|
||||
localStorage.setItem('sts', encodeToken);
|
||||
sessionStorage.setItem('tk', encodeToken);
|
||||
firstClick(token);
|
||||
const energyCode = btoa(energy.toString());
|
||||
sessionStorage.setItem('eg', energyCode);
|
||||
} else {
|
||||
const mult = sessionStorage.getItem('mt');
|
||||
if (mult) {
|
||||
const encodeMult = atob(mult);
|
||||
dispatch<any>(saveMult(Number(encodeMult)));
|
||||
} else {
|
||||
firstClick(token);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
localStorage.setItem('eg', '200'); //energy
|
||||
localStorage.setItem('sts', encodeToken);
|
||||
sessionStorage.setItem('tk', encodeToken);
|
||||
firstClick(token);
|
||||
const energyCode = btoa(energy.toString());
|
||||
sessionStorage.setItem('eg', energyCode);
|
||||
}
|
||||
//
|
||||
let avatar = user.avatar;
|
||||
if (!avatar) {
|
||||
avatar = '';
|
||||
}
|
||||
const userData = {
|
||||
tgId: user.tg_id,
|
||||
|
@ -109,8 +151,8 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
|
|||
dispatch(meRequestError(String(err)));
|
||||
}
|
||||
})
|
||||
}*/
|
||||
if (tgId && URL && !meData.avatar) {
|
||||
}
|
||||
/*if (tgId && URL && !meData.username) {
|
||||
axios.get(`${URL}/api/v1/users/get-token/123456`, {
|
||||
headers: {
|
||||
"Content-type": "application/json"
|
||||
|
@ -119,9 +161,15 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
|
|||
).then((resp) => {
|
||||
const token = resp.data.token;
|
||||
getState().token = token;
|
||||
if (token && !meData.avatar) {
|
||||
if (token && !meData.username) {
|
||||
dispatch(meRequest());
|
||||
axios.get(`${URL}/api/v1/users/${tgId}/`, {
|
||||
let urlUser = '';
|
||||
if (referral.length != 0) {
|
||||
urlUser = `${URL}/api/v1/users/${tgId}?referred_by=${referral}`;
|
||||
} else {
|
||||
urlUser = `${URL}/api/v1/users/${tgId}/`;
|
||||
}
|
||||
axios.get(urlUser, {
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
"Authorization": `TelegramToken ${token}`
|
||||
|
@ -129,8 +177,11 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
|
|||
},
|
||||
).then((resp) => {
|
||||
const user = resp.data;
|
||||
const encodeToken = btoa(unescape(encodeURIComponent(token)));
|
||||
const savedToken = localStorage.getItem('sts');
|
||||
let avatar = user.avatar;
|
||||
avatar = null;
|
||||
if (!avatar) {
|
||||
avatar = '';
|
||||
}
|
||||
axios.get(`${URLClick}/api/v1/energy`, {
|
||||
headers: {
|
||||
//"Content-type": "application/json",
|
||||
|
@ -139,19 +190,35 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
|
|||
},
|
||||
).then((resp) => {
|
||||
const energy = resp.data.energy;
|
||||
//
|
||||
const encodeToken = btoa(unescape(encodeURIComponent(token)));
|
||||
const savedToken = sessionStorage.getItem('tk');
|
||||
if (savedToken) {
|
||||
if (savedToken != encodeToken) {
|
||||
localStorage.setItem('eg', '200'); //enegry
|
||||
localStorage.setItem('sts', encodeToken);
|
||||
sessionStorage.setItem('tk', encodeToken);
|
||||
firstClick(token);
|
||||
const energyCode = btoa(energy.toString());
|
||||
sessionStorage.setItem('eg', energyCode);
|
||||
} else {
|
||||
const mult = sessionStorage.getItem('mt');
|
||||
if (mult) {
|
||||
const encodeMult = atob(mult);
|
||||
dispatch<any>(saveMult(Number(encodeMult)));
|
||||
} else {
|
||||
firstClick(token);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
localStorage.setItem('eg', '200'); //energy
|
||||
localStorage.setItem('sts', encodeToken);
|
||||
sessionStorage.setItem('tk', encodeToken);
|
||||
firstClick(token);
|
||||
const energyCode = btoa(energy.toString());
|
||||
sessionStorage.setItem('eg', energyCode);
|
||||
}
|
||||
//
|
||||
const userData = {
|
||||
tgId: user.tg_id,
|
||||
username: user.username,
|
||||
avatar: user.avatar,
|
||||
avatar: avatar,
|
||||
energy: energy.toString(), //user.energy
|
||||
points: user.points,
|
||||
name: `${firstName} ${secondName}`
|
||||
|
@ -178,7 +245,7 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
|
|||
console.log(err);
|
||||
dispatch(meRequestError(String(err)));
|
||||
})
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
|
|
19
frontend/src/store/mult.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { Action, ActionCreator } from "redux";
|
||||
import { ThunkAction } from "redux-thunk";
|
||||
import { RootState } from "./reducer";
|
||||
|
||||
export const SET_MULT = 'SET_MULT';
|
||||
|
||||
export type SetMultAction = {
|
||||
type: typeof SET_MULT,
|
||||
mult: number
|
||||
};
|
||||
|
||||
export const setMult: ActionCreator<SetMultAction > = (mult: number) => ({
|
||||
type: SET_MULT,
|
||||
mult
|
||||
});
|
||||
|
||||
export const saveMult = (mult: number): ThunkAction<void, RootState, unknown, Action<string>> => (dispatch, getState) => {
|
||||
dispatch(setMult(mult));
|
||||
}
|
|
@ -3,6 +3,8 @@ import { SET_TOKEN } from './token';
|
|||
import { IUserTg, SET_USER_TG } from './userTg';
|
||||
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';
|
||||
|
||||
export type RootState = {
|
||||
url: string,
|
||||
|
@ -11,7 +13,9 @@ export type RootState = {
|
|||
token: string,
|
||||
userTg: IUserTg,
|
||||
styleIndex: number,
|
||||
me: MeState
|
||||
me: MeState,
|
||||
referral: string,
|
||||
mult: number
|
||||
};
|
||||
|
||||
//'http://127.0.0.1:8000'
|
||||
|
@ -33,6 +37,8 @@ const initialState: RootState = {
|
|||
error: '',
|
||||
data: {}
|
||||
},
|
||||
referral: '',
|
||||
mult: 1
|
||||
};
|
||||
|
||||
export const RESET_STATE = 'RESET_STATE';
|
||||
|
@ -53,6 +59,16 @@ export const rootReducer: Reducer<RootState> = (state = initialState, action) =>
|
|||
...state,
|
||||
userTg: action.userTg
|
||||
};
|
||||
case SET_REFERRAL:
|
||||
return {
|
||||
...state,
|
||||
referral: action.referral
|
||||
};
|
||||
case SET_MULT:
|
||||
return {
|
||||
...state,
|
||||
mult: action.mult
|
||||
};
|
||||
case ME_REQUEST:
|
||||
case ME_REQUEST_SUCCESS:
|
||||
case ME_REQUEST_ERROR:
|
||||
|
|
22
frontend/src/store/referral.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { Action, ActionCreator } from "redux";
|
||||
import { ThunkAction } from "redux-thunk";
|
||||
import { RootState } from "./reducer";
|
||||
|
||||
export const SET_REFERRAL = 'SET_REFERRAL';
|
||||
|
||||
export type SetReferralAction = {
|
||||
type: typeof SET_REFERRAL,
|
||||
referral: string
|
||||
};
|
||||
|
||||
export const setReferral: ActionCreator<SetReferralAction > = (referral: string) => ({
|
||||
type: SET_REFERRAL,
|
||||
referral
|
||||
});
|
||||
|
||||
export const saveReferral = (referral: string): ThunkAction<void, RootState, unknown, Action<string>> => (dispatch, getState) => {
|
||||
const savedReferral = getState().referral;
|
||||
if(savedReferral.length === 0) {
|
||||
dispatch(setReferral(referral));
|
||||
}
|
||||
}
|
|
@ -6,10 +6,9 @@ export const verificationTg = () => {
|
|||
};
|
||||
let token = '';
|
||||
|
||||
console.log(`window.Telegram из verification ${window.Telegram}`);
|
||||
|
||||
if(window.Telegram) {
|
||||
const tg = window.Telegram.WebApp;
|
||||
tg.BackButton.show();
|
||||
tg.expand();
|
||||
tg.setBackgroundColor("#222222");
|
||||
const tgData = tg.initDataUnsafe;
|
||||
|
@ -41,14 +40,11 @@ export const verificationTg = () => {
|
|||
token = btoa(
|
||||
unescape(encodeURIComponent(validation))
|
||||
);
|
||||
console.log(`token1 ${token}`);
|
||||
user.id = tg.initDataUnsafe?.user?.id;
|
||||
user.firstName = tg.initDataUnsafe?.user?.first_name;
|
||||
user.lastName = tg.initDataUnsafe?.user?.last_name;
|
||||
}
|
||||
|
||||
console.log(`token2 ${token}`);
|
||||
|
||||
//локально
|
||||
/*token = "TelegramToken";
|
||||
user.id = "123456";
|
||||
|
|