added energy, mult storage, button and profile | also fixed bugs

This commit is contained in:
Arseniy Sitnikov 2024-12-11 05:02:21 +03:00
parent e9e0729a77
commit 26633b4b55
53 changed files with 634 additions and 160 deletions

BIN
frontend/.DS_Store vendored

Binary file not shown.

View File

@ -3,10 +3,8 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<meta <script src="https://telegram.org/js/telegram-web-app.js"></script>
name="viewport" <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
content="minimum-scale=1, initial-scale=1, width=device-width"
/>
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
name="description" name="description"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

BIN
frontend/src/.DS_Store vendored

Binary file not shown.

View File

@ -32,6 +32,7 @@ function AppComponent() {
<Route path='/referral' element={<RoutePage page='referral' />} /> <Route path='/referral' element={<RoutePage page='referral' />} />
<Route path='/auction' element={<RoutePage page='auction' />} /> <Route path='/auction' element={<RoutePage page='auction' />} />
<Route path='/styles' element={<RoutePage page='styles' />} /> <Route path='/styles' element={<RoutePage page='styles' />} />
<Route path='*' element={<RoutePage page='main' />} />
</Routes> </Routes>
<AuctionMainPopups /> <AuctionMainPopups />
</Layout> </Layout>

View File

@ -82,6 +82,7 @@
--grey22: #222222; --grey22: #222222;
--grey9A: #9A9A9A; --grey9A: #9A9A9A;
--grey93: #939393; --grey93: #939393;
--grey1F: #1F1F1F;
} }
body { body {

View File

@ -8,14 +8,16 @@ import { useDispatch } from 'react-redux';
import { updateCoinsRequestAsync } from '../../../store/me/actions'; import { updateCoinsRequestAsync } from '../../../store/me/actions';
import axios from 'axios'; import axios from 'axios';
import { DevPopup } from '../../Elements/DevPopup'; import { DevPopup } from '../../Elements/DevPopup';
import { saveMult } from '../../../store/mult';
interface IClickerBtn { interface IClickerBtn {
coins: number, coins: number,
setCoins(a: number): void, 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 urlClick = useAppSelector<string>(state => state.urlClick);
const token = useAppSelector<string>(state => state.token); const token = useAppSelector<string>(state => state.token);
const [fill, setFill] = useState(0); const [fill, setFill] = useState(0);
@ -27,14 +29,20 @@ export function ClickerBtn({ coins, setCoins, energy }: IClickerBtn) {
const [gradient, setGradient] = useState(getGradient()); const [gradient, setGradient] = useState(getGradient());
let styleIndex = useAppSelector<number>(state => state.styleIndex); let styleIndex = useAppSelector<number>(state => state.styleIndex);
const [initEnergy, setEnergy] = useState(energy); 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 [closeError, setCloseError] = useState(true);
const [error, setError] = useState(false); const [error, setError] = useState(false);
const [animClose, setAnimClose] = useState(false); const [animClose, setAnimClose] = useState(false);
const dispatch = useDispatch(); const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
setFill((maxEnergy - initEnergy)/maxEnergy * 100); const savedEnergy = sessionStorage.getItem('eg');
if(savedEnergy) {
const encodeEnergy = atob(savedEnergy);
setMaxEnergy(Number(encodeEnergy));
}
setFill((maxEnergy - initEnergy) / maxEnergy * 100);
}, []); }, []);
useEffect(() => { useEffect(() => {
@ -42,54 +50,36 @@ export function ClickerBtn({ coins, setCoins, energy }: IClickerBtn) {
}, [styleIndex]); }, [styleIndex]);
const btnClick = () => { const btnClick = () => {
sendClick(); if (!(initEnergy === 0)) {
/*if(!error) {
sendClick(); 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 { } else {
sendClick(); setClose(false);
}*/ }
}; };
const sendClick = () => { const sendClick = () => {
if(urlClick && token) { if(token) {
axios.get(`${urlClick}/api/v1/click`, { axios.post(`${urlClick}/api/v1/click/`,
headers: { {},
//"Content-type": "application/json", {
"Authorization": `TelegramToken ${token}` headers: {
"Authorization": `TelegramToken ${token}`
}
} }
},
).then((resp) => { ).then((resp) => {
console.log(resp); //console.log(resp);
if(resp.data) { if(resp.data) {
const click = Number(resp.data.click.value); 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; const newFill = (maxEnergy - newEnergy) / maxEnergy * 100;
if (newFill <= 100) { if (newFill <= 100) {
const newCoins = coins + click; const newCoins = Number(coins + click);
dispatch<any>(updateCoinsRequestAsync(newCoins, newEnergy)) dispatch<any>(updateCoinsRequestAsync(newCoins, newEnergy))
setCoins(newCoins); setCoins(newCoins);
setEnergy(newEnergy) setEnergy(newEnergy)

View File

@ -2,6 +2,7 @@
position: relative; position: relative;
width: 270px; width: 270px;
height: 270px; height: 270px;
user-select: none;
} }
.ringContainer::before { .ringContainer::before {
@ -17,6 +18,7 @@
} }
.ringBig { .ringBig {
user-select: none;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -27,6 +29,7 @@
} }
.ringSmall { .ringSmall {
user-select: none;
z-index: 1; z-index: 1;
position: absolute; position: absolute;
top: 50%; top: 50%;

View File

@ -13,7 +13,7 @@ export function ClickerBtnFooter({ text, className, onClick }: IClickerBtnFooter
return ( return (
<div className={`${styles.container} ${className}`} onClick={onClick}> <div className={`${styles.container} ${className}`} onClick={onClick}>
<div className={styles.content}> <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> <p style={ETextStyles.RwSb14120} className={styles.text}>{text}</p>
</div> </div>
</div> </div>

View File

@ -16,9 +16,9 @@ export function ClickerFooter() {
<div className={styles.container}> <div className={styles.container}>
<ClickerBtnFooter text='Стили' className={styles.btn} onClick={() => navigate('/styles')}/> <ClickerBtnFooter text='Стили' className={styles.btn} onClick={() => navigate('/styles')}/>
<ClickerBtnFooter text='Аукцион' className={styles.btn} onClick={() => { !isDev ? navigate('/auction') : setCloseDev(false) }}/> <ClickerBtnFooter text='Аукцион' className={styles.btn} onClick={() => { !isDev ? navigate('/auction') : setCloseDev(false) }}/>
<div className={styles.fire}> { !isDev && <div className={styles.fire}>
<Icon icon={EIcons.FireIcon}/> <Icon icon={EIcons.FireIcon}/>
</div> </div>}
{!closeDev && <ModalWindow removeBtn={true} setCloseAnimOut={setCloseAnimOut} closeAnimOut={closeAnimOut} setClose={setCloseDev} modalBlock={ {!closeDev && <ModalWindow removeBtn={true} setCloseAnimOut={setCloseAnimOut} closeAnimOut={closeAnimOut} setClose={setCloseDev} modalBlock={
<DevPopup setClose={setCloseAnimOut} type='dev' /> <DevPopup setClose={setCloseAnimOut} type='dev' />
} />} } />}

View 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>
);
}

View File

@ -0,0 +1 @@
export * from './PointsZoom';

View 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;
}
}

View File

@ -3,6 +3,7 @@ import styles from './profile.module.css';
import { ETextStyles } from '../../texts'; import { ETextStyles } from '../../texts';
import { formatNumber } from '../../../utils/formatNumber'; import { formatNumber } from '../../../utils/formatNumber';
import { PersonIcon } from '../../Elements/PersonIcon'; import { PersonIcon } from '../../Elements/PersonIcon';
import { EIcons, Icon } from '../../Icons';
interface IProfileClicker { interface IProfileClicker {
name: string, name: string,
@ -14,12 +15,12 @@ interface IProfileClicker {
export function Profile({ name, points, img, className }: IProfileClicker) { export function Profile({ name, points, img, className }: IProfileClicker) {
return ( return (
<div className={`${styles.container} ${className}`}> <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}> <div className={styles.content}>
<p style={ETextStyles.RwSb12120} className={styles.name}>{name}</p> <p style={ETextStyles.RwSb12120} className={styles.name}>{name}</p>
<div className={styles.pointsContainer}> <div className={styles.pointsContainer}>
<p className={styles.points} style={ETextStyles.InSb10120}> <p className={styles.points} style={ETextStyles.InSb10120}>
{formatNumber(points)} {formatNumber(Number(points.toFixed(2)))}
</p> </p>
<div className={styles.icon} style={{ backgroundImage: "url('assets/btnIcon.png')"}}></div> <div className={styles.icon} style={{ backgroundImage: "url('assets/btnIcon.png')"}}></div>
</div> </div>

View File

@ -38,3 +38,13 @@
.content { .content {
margin-left: 4px; margin-left: 4px;
} }
.emptyIcon {
display: flex;
align-items: center;
justify-content: center;
width: 25px;
height: 25px;
background-color: var(--grey12);
border-radius: 50%;
}

View File

@ -10,7 +10,11 @@ import { UsersIcons } from '../../Elements/UsersIcons';
import { formatNumber } from '../../../utils/formatNumber'; import { formatNumber } from '../../../utils/formatNumber';
import { DevPopup } from '../../Elements/DevPopup'; import { DevPopup } from '../../Elements/DevPopup';
export function SectionsBlock() { interface ISectionsBlock {
mult:number;
}
export function SectionsBlock({ mult }: ISectionsBlock) {
const scaleRef = 70; const scaleRef = 70;
const [close, setClose] = useState(true); const [close, setClose] = useState(true);
const navigate = useNavigate(); const navigate = useNavigate();
@ -41,20 +45,23 @@ export function SectionsBlock() {
<div className={styles.sectionContainer}> <div className={styles.sectionContainer}>
<div className={styles.leftContainer}> <div className={styles.leftContainer}>
<CardSection title='Место в топе' onClick={() => {!isDev ? navigate('/rating') : setCloseDev(false)}}> <CardSection title='Место в топе' onClick={() => {!isDev ? navigate('/rating') : setCloseDev(false)}}>
{!isDev && <div className={styles.bottomRank}> {<div className={`${styles.bottomRank} ${isDev ? styles.dev : ''}`}>
<div style={ETextStyles.InSb12120}> <div style={ETextStyles.InSb12120}>
<span className={styles.rank1}>#</span> <span className={styles.rank1}>#</span>
<span>{formatNumber(12980)}</span> <span>{formatNumber(1)}</span>
</div> </div>
<UsersIcons size={16}/> <UsersIcons size={16}/>
</div>} </div>}
</CardSection> </CardSection>
<CardSection title='Множитель' onClick={() => { !isDev ? setClose(false) : setCloseDev(false) }}> <CardSection title='Множитель' onClick={() => { setClose(false) }}>
{!isDev &&<PointsBlock points='1.50' />} <p style={ETextStyles.InSb12120}>
<span style={{color: 'var(--primary)'}}>{'X '}</span>
{mult}
</p>
</CardSection> </CardSection>
</div> </div>
<CardSection title='Реферальное хранилище' className={styles.rigthEl} onClick={() => { !isDev ? navigate('/referral') : setCloseDev(false) }}> <CardSection title='Реферальное хранилище' className={styles.rigthEl} onClick={() => { navigate('/referral') }}>
{!isDev &&<div> {<div className={isDev ? styles.dev : ''}>
<PointsBlock points={formatNumber(800)} className={styles.scalePoints} /> <PointsBlock points={formatNumber(800)} className={styles.scalePoints} />
<div className={styles.scaleContainer}> <div className={styles.scaleContainer}>
<div className={styles.scale} style={{ width: `${scaleRef}px` }}></div> <div className={styles.scale} style={{ width: `${scaleRef}px` }}></div>

View File

@ -55,3 +55,19 @@
height: 10px; height: 10px;
background: var(--primary); 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);
}

View File

@ -14,9 +14,9 @@ export function DevPopup({ setClose, type }: IDevPopup) {
<div className={styles.iconContainer}> <div className={styles.iconContainer}>
<div className={styles.icon} style={{backgroundImage: "url('assets/dev.png')"}}></div> <div className={styles.icon} style={{backgroundImage: "url('assets/dev.png')"}}></div>
</div> </div>
<h2 className={styles.title} style={ETextStyles.RwSb24100}>{type === 'dev' ? 'Скоро будет доступно' : 'Возникла ошибка'}</h2> <h2 className={styles.title} style={ETextStyles.RwSb24100}>{type === 'dev' ? 'Скоро откроем' : 'Возникла ошибка'}</h2>
<p className={styles.text} style={ETextStyles.RwSb14120}>{type === 'dev' ? 'Пока что делаем эту фичу. Скоро сможете поюзать.' : 'Мы пока не можем принимать клики, но скоро всё починим.'}</p> <p className={styles.text} style={ETextStyles.RwSb14120}>{type === 'dev' ? <span>Подготавливаем что-то особенное для вас. Скоро увидимся на&nbsp;новом уровне!</span> : 'Мы пока не можем принимать клики, но скоро всё починим.'}</p>
<Button text={type === 'dev' ? 'Продолжить кликать' : 'Принято'} stroke={true} onClick={() => setClose(true)}/> <Button text={type === 'dev' ? 'Продолжить кликать' : 'Принято'} onClick={() => setClose(true)}/>
</div> </div>
); );
} }

View File

@ -73,7 +73,7 @@ export const StylesSwiper: React.FC<IStylesSwiper> = memo(({ selectedStyle, setC
transform: `rotate(${isActive ? 0 : deg}deg)`, transform: `rotate(${isActive ? 0 : deg}deg)`,
filter: `blur(${isActive ? 0 : 3}px)` 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>))} </SwiperSlide>))}

View File

@ -24,33 +24,33 @@
background: var(--gradientBlue); background: var(--gradientBlue);
} }
.card1 div { /*.card1 div {
background-image: url('assets/style1.png'); background-image: url('assets/style1.png');
} }*/
.card2 { .card2 {
background: var(--gradientOrange); background: var(--gradientOrange);
} }
.card2 div { /*.card2 div {
background-image: url('assets/style2.png'); background-image: url('assets/style2.png');
} }*/
.card3 { .card3 {
background: var(--gradientYellow); background: var(--gradientYellow);
} }
.card3 div { /*.card3 div {
background-image: url('assets/style3.png'); background-image: url('assets/style3.png');
} }*/
.card4 { .card4 {
background: var(--gradientOrangeYellow); background: var(--gradientOrangeYellow);
} }
.card4 div { /*.card4 div {
background-image: url('assets/style4.png'); background-image: url('assets/style4.png');
} }*/
.disabled { .disabled {
position: relative; position: relative;

View File

@ -6,7 +6,7 @@
position: absolute; position: absolute;
top: 0; top: 0;
border-radius: 50%; border-radius: 50%;
background-color: var(--white); background-color: var(--grey6C);
border: 1px solid var(--grey12); border: 1px solid var(--grey12);
} }

View File

@ -1,5 +1,5 @@
import * as React from 'react'; 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'; import { MedalFirstIcon, MedalSecondIcon, MedalThirdIcon } from './medals';
interface IconsProps { interface IconsProps {
icon: EIcons; icon: EIcons;
@ -17,7 +17,9 @@ export const EIcons = {
TrendIcon: <TrendIcon/>, TrendIcon: <TrendIcon/>,
ChartIcon: <ChartIcon/>, ChartIcon: <ChartIcon/>,
CopyIcon: <CopyIcon/>, CopyIcon: <CopyIcon/>,
UpPriceIcon: <UpPriceIcon/> UpPriceIcon: <UpPriceIcon/>,
ProfileIcon: <ProfileIcon/>,
StyleIcon: <StyleIcon />
} as const; } as const;
type EIcons = typeof EIcons[keyof typeof EIcons]; type EIcons = typeof EIcons[keyof typeof EIcons];

View File

@ -77,3 +77,21 @@ export function UpPriceIcon() {
</svg> </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>
)
}

View File

@ -15,11 +15,21 @@ interface IModalWindow {
export function ModalWindow({ modalBlock, setClose, removeBtn, closeAnimOut, setCloseAnimOut }: IModalWindow) { export function ModalWindow({ modalBlock, setClose, removeBtn, closeAnimOut, setCloseAnimOut }: IModalWindow) {
const node = document.querySelector('#modal_root'); const node = document.querySelector('#modal_root');
const [closeAnim, setCloseAnim] = useState(false); const [closeAnim, setCloseAnim] = useState(false);
const html = document.querySelector('html');
useEffect(() => {
if (html) {
html.style.overflowY = 'hidden';
}
}, []);
if (!node) return null; if (!node) return null;
const closePopUp = () => { const closePopUp = () => {
setCloseAnim(true); setCloseAnim(true);
if (html) {
html.style.overflowY = 'auto';
}
const timer = setTimeout(() => { const timer = setTimeout(() => {
setClose(true); setClose(true);

View File

@ -6,6 +6,10 @@ import { ETextStyles } from '../../texts';
import { SectionsBlock } from '../../Clicker/SectionsBlock'; import { SectionsBlock } from '../../Clicker/SectionsBlock';
import { ClickerFooter } from '../../Clicker/ClickerFooter'; import { ClickerFooter } from '../../Clicker/ClickerFooter';
import { StyleElements } from '../../Clicker/StyleElements'; 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 { interface IClickerPageInterface {
name: string, name: string,
@ -17,16 +21,42 @@ interface IClickerPageInterface {
export function ClickerPage({ name, points, img, energy }: IClickerPageInterface) { export function ClickerPage({ name, points, img, energy }: IClickerPageInterface) {
const styleIndex = Number(localStorage.getItem('selectedStyle')); const styleIndex = Number(localStorage.getItem('selectedStyle'));
const [coins, setCoins] = useState(points); 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 ( return (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.records}> <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}/> <Profile name={name} points={coins} className={styles.profile} img={img}/>
<h1 style={ETextStyles.RwSb24100} className={styles.title}>Мои рекорды</h1> <h1 style={ETextStyles.RwSb24100} className={styles.title}>Мои рекорды</h1>
<SectionsBlock /> <SectionsBlock mult={mult}/>
</div> </div>
<div className={styles.clicker}> <div className={styles.clicker} style={{height: `${height > 670 && 'calc(100vh - 355px)'}`}}>
<ClickerBtn coins={coins} setCoins={setCoins} energy={energy}/> <ClickerBtn coins={coins} setCoins={setCoins} energy={energy} setMult={setMult}/>
</div> </div>
<ClickerFooter /> <ClickerFooter />
{styleIndex != 0 && <div> {styleIndex != 0 && <div>

View File

@ -30,7 +30,7 @@
.clicker { .clicker {
position: relative; position: relative;
z-index: 5; z-index: 5;
margin-bottom: 24px; margin-bottom: 100px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -39,3 +39,10 @@
.profile { .profile {
margin-bottom: 8px; margin-bottom: 8px;
} }
/*.pointsAnim {
position: absolute;
width: 100%;
top: 0;
left: 0;
}*/

View File

@ -12,6 +12,7 @@ import { useUserData } from '../../hooks/useUserData';
import { Spinner } from '../../Elements/Spinner'; import { Spinner } from '../../Elements/Spinner';
import { updateBackground } from '../../../utils/updateBackground'; import { updateBackground } from '../../../utils/updateBackground';
import { ErrorPage } from '../ErrorPage'; import { ErrorPage } from '../ErrorPage';
import { useNavigate } from 'react-router-dom';
interface IRoutePage { interface IRoutePage {
page: string page: string
@ -20,26 +21,37 @@ interface IRoutePage {
export function RoutePage({ page }: IRoutePage) { export function RoutePage({ page }: IRoutePage) {
const verified = useTgData(); const verified = useTgData();
const { dataUser, loadingUser, errorUser } = useUserData(); const { dataUser, loadingUser, errorUser } = useUserData();
const navigate = useNavigate();
//@ts-ignore
const tg = window.Telegram.WebApp;
var BackButton = tg.BackButton;
useEffect(() => { useEffect(() => {
updateBackground(page); updateBackground(page);
updateStyles(); updateStyles();
if(page === 'main') {
BackButton.hide();
} else {
BackButton.show();
}
}, [page]); }, [page]);
//{!verified ? <WrongSourcePage/> : BackButton.onClick(function () {
//} navigate(-1);
});
return ( return (
<div> <div>
<div> {!verified ? <WrongSourcePage /> : <div>
{page === 'main' && !loadingUser && !errorUser && dataUser.name && dataUser.avatar && <ClickerPage name={dataUser.name} points={Number(dataUser.points)} img={dataUser.avatar} energy={Number(dataUser.energy)}/>} { //@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 === 'rating' && !loadingUser && !errorUser && <RatingPage />}
{page === 'referral' && !loadingUser && !errorUser && <StoragePage />} {page === 'referral' && !loadingUser && !errorUser && <StoragePage />}
{page === 'auction' && !loadingUser && !errorUser && <AuctionPage />} {page === 'auction' && !loadingUser && !errorUser && <AuctionPage />}
{page === 'styles' && !loadingUser && !errorUser && <StylesPage />} {page === 'styles' && !loadingUser && !errorUser && <StylesPage />}
{(loadingUser) && <div className={styles.spinnerContainer}><Spinner color='#FFFFFF' size='50px' thickness='6px' className={styles.spinner} /></div> } {(loadingUser) && <div className={styles.spinnerContainer}><Spinner color='#FFFFFF' size='50px' thickness='6px' className={styles.spinner} /></div> }
{errorUser && !loadingUser && <ErrorPage detail={errorUser}/>} {errorUser && !loadingUser && <ErrorPage detail={errorUser}/>}
</div> </div>}
</div> </div>
); );
} }

View File

@ -2,30 +2,41 @@ import React, { useEffect, useState } from 'react';
import styles from './storagepage.module.css'; import styles from './storagepage.module.css';
import { ETextStyles } from '../../texts'; import { ETextStyles } from '../../texts';
import { StorageBtn } from '../../Storage/StorageBtn'; import { StorageBtn } from '../../Storage/StorageBtn';
import { StorageScale } from '../../Storage/StorageScale';
import { PopupCard } from '../../Elements/PopupCard';
import { Button } from '../../Button'; import { Button } from '../../Button';
import { EIcons, Icon } from '../../Icons'; import { EIcons, Icon } from '../../Icons';
import { сopyTextToClipboard } from '../../../utils/copyText'; import { сopyTextToClipboard } from '../../../utils/copyText';
import { Notification } from '../../Notification'; import { Notification } from '../../Notification';
import { StoragePageBlock } from '../../Storage/StoragePageBlock'; import { StoragePageBlock } from '../../Storage/StoragePageBlock';
import { FriendsPageBlock } from '../../Storage/FriendsPageBlock'; import { FriendsPageBlock } from '../../Storage/FriendsPageBlock';
import { useAppSelector } from '../../hooks/useAppSelector';
import { DevPopup } from '../../Elements/DevPopup';
import { ModalWindow } from '../../ModalWindow';
export function StoragePage() { export function StoragePage() {
const userId = useAppSelector<string>(state => state.userTg.id);
const [page, setPage] = useState('storage'); 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 [showNotif, setShow] = useState(false);
const [closeAnimOut, setCloseAnimOut] = useState(false);
const [closeDev, setCloseDev] = useState(true);
return ( return (
<div> <div>
<h1 style={ETextStyles.RwSb30100} className={styles.title}>Реферальная программа</h1> <h1 style={ETextStyles.RwSb30100} className={styles.title}>Реферальная программа</h1>
<div className={styles.btnGroup}> <div className={styles.btnGroup}>
<StorageBtn active={page === 'storage'} type={'storage'} onClick={() => setPage('storage')}/> <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> </div>
{page === 'storage' ? <StoragePageBlock/> : <FriendsPageBlock/>} {page === 'storage' ? <StoragePageBlock/> : <FriendsPageBlock/>}
<Button className={styles.btn} icon={<Icon icon={EIcons.CopyIcon} />} text='Пригласить друга' onClick={() => { сopyTextToClipboard(refLink); setShow(true)}}/> <Button className={styles.btn} icon={<Icon icon={EIcons.CopyIcon} />} text='Пригласить друга' onClick={() => { сopyTextToClipboard(refLink); setShow(true)}}/>
{showNotif && <Notification title='Успешно' text='Пригласительная ссылка скопирована' setShow={setShow} />} {showNotif && <Notification title='Успешно' text='Пригласительная ссылка скопирована' setShow={setShow} />}
{!closeDev && <ModalWindow removeBtn={true} setCloseAnimOut={setCloseAnimOut} closeAnimOut={closeAnimOut} setClose={setCloseDev} modalBlock={
<DevPopup setClose={setCloseAnimOut} type='dev' />
} />}
</div> </div>
); );
} }

View File

@ -8,9 +8,11 @@ export function WrongSourcePage() {
const { width, height } = useWindowSize(); const { width, height } = useWindowSize();
return ( return (
<div className={styles.container} style={{height: `${height}px`}}> <div className={styles.container} style={{ height: `${height}px` }}>
<h1 style={ETextStyles.RwSb24100} className={styles.title}>Похоже вы вошли не по той ссылке...</h1> <div className={styles.innerContainer}>
<Button text='Войти через Telegram'/> <h1 style={ETextStyles.RwSb24100} className={styles.title}>Похоже вы вошли не по той ссылке...</h1>
<Button text='Войти через Telegram' onClick={() => document.location.href = 'https://t.me/sapphirecrown_bot'}/>
</div>
</div> </div>
); );
} }

View File

@ -4,7 +4,15 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
}
.innerContainer {
max-width: 400px;
gap: 30px; gap: 30px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
} }
.title { .title {

View File

@ -6,14 +6,15 @@ import { EIcons, Icon } from '../../Icons';
interface IStorageBtn { interface IStorageBtn {
type: 'storage' | 'friends', type: 'storage' | 'friends',
active: boolean, 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')); const selectedStyle = Number(localStorage.getItem('selectedStyle'));
return ( 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}> <div className={styles.content}>
{type === 'storage' ? <Icon icon={EIcons.TrendIcon} /> : <Icon icon={EIcons.ChartIcon} />} {type === 'storage' ? <Icon icon={EIcons.TrendIcon} /> : <Icon icon={EIcons.ChartIcon} />}
<p className={selectedStyle === 2 ? styles.darkText : ''}>{type === 'storage' ? 'Хранилище' : 'Друзья'}</p> <p className={selectedStyle === 2 ? styles.darkText : ''}>{type === 'storage' ? 'Хранилище' : 'Друзья'}</p>

View File

@ -22,5 +22,21 @@
} }
.stroke { .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;
} }

View File

@ -1,22 +1,33 @@
import React from 'react'; import React, { useState } from 'react';
import styles from './storagepageblock.module.css'; import styles from './storagepageblock.module.css';
import { StorageScale } from '../StorageScale'; import { StorageScale } from '../StorageScale';
import { ETextStyles } from '../../texts'; import { ETextStyles } from '../../texts';
import { PopupCard } from '../../Elements/PopupCard'; import { PopupCard } from '../../Elements/PopupCard';
import { ModalWindow } from '../../ModalWindow';
import { DevPopup } from '../../Elements/DevPopup';
export function StoragePageBlock() { export function StoragePageBlock() {
const [closeAnimOut, setCloseAnimOut] = useState(false);
const [closeDev, setCloseDev] = useState(true);
const isDev = true;
return ( return (
<div> <div>
<h2 style={ETextStyles.RwSb18120} className={styles.title}>Хранилище</h2> <h2 style={ETextStyles.RwSb18120} className={styles.title}>Хранилище</h2>
<StorageScale points='400' percent={70} className={styles.scale} /> <div className={`${styles.containerStorage}`} onClick={() => setCloseDev(false)}>
<p style={ETextStyles.RwRg10120} className={styles.descr}> <StorageScale points='0' percent={0} className={styles.scale} isDev={true}/>
В&nbsp;хранилище приходит часть коинов, заработанная вашими друзьями. Считаем так: количество коинов * 5%. Хранилище пополняется каждый вечер. {!isDev && <p style={ETextStyles.RwRg10120} className={styles.descr}>
</p> В&nbsp;хранилище приходит часть коинов, заработанная вашими друзьями. Считаем так: количество коинов * 5%. Хранилище пополняется каждый вечер.
</p>}
{isDev && <div style={{height: '30px'}}></div> }
</div>
<h2 style={ETextStyles.RwSb18120} className={styles.title}>Как пригласить друга?</h2> <h2 style={ETextStyles.RwSb18120} className={styles.title}>Как пригласить друга?</h2>
<div className={styles.cards}> <div className={styles.cards}>
<PopupCard img='assets/Chain.png' title='Отправляй ссылку другу' text={<span>Друг присоединяется по&nbsp;пригласительной ссылке и&nbsp;становится рефералом, как только совершает активность в&nbsp;приложении.</span>} /> <PopupCard img='assets/Chain.png' title='Отправляй ссылку другу' text={<span>Друг присоединяется по&nbsp;пригласительной ссылке и&nbsp;становится рефералом, как только совершает активность в&nbsp;приложении.</span>} />
<PopupCard img='assets/Money.png' title='Зарабатывайте вместе' text={<span>Друг кликает, ты&nbsp;получаешь&nbsp;5% его кликов, а&nbsp;он&nbsp;3% с&nbsp;твоих. Не&nbsp;забывай забирать коины из&nbsp;хранилища!</span>} /> <PopupCard img='assets/Money.png' title='Зарабатывайте вместе' text={<span>Друг кликает, ты&nbsp;получаешь&nbsp;5% его кликов, а&nbsp;он&nbsp;3% с&nbsp;твоих. Не&nbsp;забывай забирать коины из&nbsp;хранилища!</span>} />
</div> </div>
{!closeDev && <ModalWindow removeBtn={true} setCloseAnimOut={setCloseAnimOut} closeAnimOut={closeAnimOut} setClose={setCloseDev} modalBlock={
<DevPopup setClose={setCloseAnimOut} type='dev' />
} />}
</div> </div>
); );
} }

View File

@ -17,3 +17,20 @@
flex-direction: column; flex-direction: column;
gap: 8px; 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);
}

View File

@ -8,9 +8,10 @@ interface IStorageScale {
percent: number, percent: number,
points: string, points: string,
className ?: 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 [showNotif, setShow] = useState(false);
const [initpercent, setPercent] = useState(percent); const [initpercent, setPercent] = useState(percent);
@ -26,8 +27,9 @@ export function StorageScale({ percent, points, className }: IStorageScale) {
<div className={styles.content} style={ETextStyles.InSb16120}> <div className={styles.content} style={ETextStyles.InSb16120}>
{initpercent ===100 && <p>Забрать</p>} {initpercent ===100 && <p>Забрать</p>}
{initpercent > 0 && <PointsBlock points={points} sizeIcon={20} sizeText={16} />} {initpercent > 0 && <PointsBlock points={points} sizeIcon={20} sizeText={16} />}
{initpercent === 0 && <div className={styles.imgVolt} style={{backgroundImage: "url('assets/Volt.png')"}}></div>} {initpercent === 0 && !isDev && <div className={styles.imgVolt} style={{backgroundImage: "url('assets/Volt.png')"}}></div>}
{initpercent === 0 && <p style={ETextStyles.InRg14120}>Больше друзей быстрее заполнение</p> } {initpercent === 0 && !isDev && <p style={ETextStyles.InRg14120}>Больше друзей быстрее заполнение</p> }
{isDev && <p style={ETextStyles.InRg14120}>Скоро откроем</p>}
</div> </div>
<div className={styles.scale} style={{ width: `${initpercent}%`}}></div> <div className={styles.scale} style={{ width: `${initpercent}%`}}></div>
{showNotif && <Notification title='Пополнение' text={`Баланс баллов увеличен на ${points}`} setShow={setShow} />} {showNotif && <Notification title='Пополнение' text={`Баланс баллов увеличен на ${points}`} setShow={setShow} />}

View 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]);
}

View File

@ -17,8 +17,6 @@ export function useTgData() {
if (savedToken.length === 0) { if (savedToken.length === 0) {
//@ts-ignore //@ts-ignore
const [user, token]: [IUserTg, string] = verificationTg(); 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) { if (token.length != 0 && user.id && user.id.length != 0) {
setVerified(true); setVerified(true);
dispatch<any>(saveToken(token)); dispatch<any>(saveToken(token));

View File

@ -3,20 +3,29 @@ import { IUserData, meRequestAsync } from "../../store/me/actions";
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useAppSelector } from './useAppSelector'; import { useAppSelector } from './useAppSelector';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { saveReferral } from '../../store/referral';
export function useUserData() { export function useUserData() {
const dataUser = useAppSelector<IUserData>(state => state.me.data); const dataUser = useAppSelector<IUserData>(state => state.me.data);
const loadingUser = useAppSelector<boolean>(state => state.me.loading); const loadingUser = useAppSelector<boolean>(state => state.me.loading);
const errorUser = useAppSelector<String>(state => state.me.error); const errorUser = useAppSelector<String>(state => state.me.error);
const token = useAppSelector<string>(state => state.token); const token = useAppSelector<string>(state => state.token);
const savedReferral = useAppSelector<string>(state => state.token);
const dispatch = useDispatch(); const dispatch = useDispatch();
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
//if (!token) navigate('/auth/welcome'); if (savedReferral.length === 0) {
//@ts-ignore const currentUrl = new URL(window.location.href);
dispatch(meRequestAsync()); const referredBy = currentUrl.searchParams.get("referred_by");
if (referredBy) {
dispatch<any>(saveReferral(referredBy))
}
}
if (dataUser.username?.length != 0) {
//@ts-ignore
dispatch(meRequestAsync());
}
}, [token]); }, [token]);
return { dataUser, loadingUser, errorUser }; return { dataUser, loadingUser, errorUser };

View File

@ -2,6 +2,7 @@ import { Action, ActionCreator } from "redux";
import { ThunkAction } from "redux-thunk"; import { ThunkAction } from "redux-thunk";
import { RootState } from "../reducer"; import { RootState } from "../reducer";
import axios from "axios"; import axios from "axios";
import { saveMult } from "../mult";
export interface IUserData { export interface IUserData {
tgId?: number; tgId?: number;
@ -54,10 +55,33 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
const token = getState().token; const token = getState().token;
const URL = getState().url; const URL = getState().url;
const URLClick = getState().urlClick; const URLClick = getState().urlClick;
//localStorage.setItem('eg', '500'); const referral = getState().referral;
/*if (tgId && URL && !meData.avatar && token.length != 0 && URLClick) {
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()); 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: { headers: {
"Content-type": "application/json", "Content-type": "application/json",
"Authorization": `TelegramToken ${token}` "Authorization": `TelegramToken ${token}`
@ -65,42 +89,60 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
}, },
).then((resp) => { ).then((resp) => {
const user = resp.data; const user = resp.data;
const encodeToken = btoa(unescape(encodeURIComponent(token)));
const savedToken = localStorage.getItem('sts');
axios.get(`${URLClick}/api/v1/energy`, { axios.get(`${URLClick}/api/v1/energy`, {
headers: { headers: {
//"Content-type": "application/json", //"Content-type": "application/json",
"Authorization": `TelegramToken ${token}` "Authorization": `TelegramToken ${token}`
} }
}, },
).then((resp) => { ).then((resp) => {
const energy = resp.data.energy; const energy = resp.data.energy;
if (savedToken) { //
if (savedToken != encodeToken) { const encodeToken = btoa(unescape(encodeURIComponent(token)));
localStorage.setItem('eg', '200'); //enegry const savedToken = sessionStorage.getItem('tk');
localStorage.setItem('sts', encodeToken); if (savedToken) {
} if (savedToken != 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 { } else {
localStorage.setItem('eg', '200'); //energy firstClick(token);
localStorage.setItem('sts', encodeToken);
} }
const userData = { }
tgId: user.tg_id, } else {
username: user.username, sessionStorage.setItem('tk', encodeToken);
avatar: user.avatar, firstClick(token);
energy: energy.toString(), //energy const energyCode = btoa(energy.toString());
points: user.points, sessionStorage.setItem('eg', energyCode);
name: `${firstName} ${secondName}` }
}; //
dispatch(meRequestSuccess(userData)); let avatar = user.avatar;
}).catch((err) => { if (!avatar) {
console.log(err); avatar = '';
if (err.response.data.detail) { }
dispatch(meRequestError(String(err.response.data.detail))); const userData = {
} else { tgId: user.tg_id,
dispatch(meRequestError(String(err))); username: user.username,
} avatar: user.avatar,
}) energy: energy.toString(), //energy
points: user.points,
name: `${firstName} ${secondName}`
};
dispatch(meRequestSuccess(userData));
}).catch((err) => {
console.log(err);
if (err.response.data.detail) {
dispatch(meRequestError(String(err.response.data.detail)));
} else {
dispatch(meRequestError(String(err)));
}
})
}).catch((err) => { }).catch((err) => {
console.log(err); console.log(err);
if (err.response.data.detail) { if (err.response.data.detail) {
@ -109,8 +151,8 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
dispatch(meRequestError(String(err))); dispatch(meRequestError(String(err)));
} }
}) })
}*/ }
if (tgId && URL && !meData.avatar) { /*if (tgId && URL && !meData.username) {
axios.get(`${URL}/api/v1/users/get-token/123456`, { axios.get(`${URL}/api/v1/users/get-token/123456`, {
headers: { headers: {
"Content-type": "application/json" "Content-type": "application/json"
@ -119,9 +161,15 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
).then((resp) => { ).then((resp) => {
const token = resp.data.token; const token = resp.data.token;
getState().token = token; getState().token = token;
if (token && !meData.avatar) { if (token && !meData.username) {
dispatch(meRequest()); 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: { headers: {
"Content-type": "application/json", "Content-type": "application/json",
"Authorization": `TelegramToken ${token}` "Authorization": `TelegramToken ${token}`
@ -129,8 +177,11 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
}, },
).then((resp) => { ).then((resp) => {
const user = resp.data; const user = resp.data;
const encodeToken = btoa(unescape(encodeURIComponent(token))); let avatar = user.avatar;
const savedToken = localStorage.getItem('sts'); avatar = null;
if (!avatar) {
avatar = '';
}
axios.get(`${URLClick}/api/v1/energy`, { axios.get(`${URLClick}/api/v1/energy`, {
headers: { headers: {
//"Content-type": "application/json", //"Content-type": "application/json",
@ -139,19 +190,35 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
}, },
).then((resp) => { ).then((resp) => {
const energy = resp.data.energy; const energy = resp.data.energy;
//
const encodeToken = btoa(unescape(encodeURIComponent(token)));
const savedToken = sessionStorage.getItem('tk');
if (savedToken) { if (savedToken) {
if (savedToken != encodeToken) { if (savedToken != encodeToken) {
localStorage.setItem('eg', '200'); //enegry sessionStorage.setItem('tk', encodeToken);
localStorage.setItem('sts', 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 { } else {
localStorage.setItem('eg', '200'); //energy sessionStorage.setItem('tk', encodeToken);
localStorage.setItem('sts', encodeToken); firstClick(token);
const energyCode = btoa(energy.toString());
sessionStorage.setItem('eg', energyCode);
} }
//
const userData = { const userData = {
tgId: user.tg_id, tgId: user.tg_id,
username: user.username, username: user.username,
avatar: user.avatar, avatar: avatar,
energy: energy.toString(), //user.energy energy: energy.toString(), //user.energy
points: user.points, points: user.points,
name: `${firstName} ${secondName}` name: `${firstName} ${secondName}`
@ -178,7 +245,7 @@ export const meRequestAsync = (): ThunkAction<void, RootState, unknown, Action<s
console.log(err); console.log(err);
dispatch(meRequestError(String(err))); dispatch(meRequestError(String(err)));
}) })
} }*/
} }

View 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));
}

View File

@ -3,6 +3,8 @@ import { SET_TOKEN } from './token';
import { IUserTg, SET_USER_TG } from './userTg'; import { IUserTg, SET_USER_TG } from './userTg';
import { MeState, meReducer } from './me/reducer'; import { MeState, meReducer } from './me/reducer';
import { ME_REQUEST, ME_REQUEST_ERROR, ME_REQUEST_SUCCESS } from './me/actions'; 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 = { export type RootState = {
url: string, url: string,
@ -11,7 +13,9 @@ export type RootState = {
token: string, token: string,
userTg: IUserTg, userTg: IUserTg,
styleIndex: number, styleIndex: number,
me: MeState me: MeState,
referral: string,
mult: number
}; };
//'http://127.0.0.1:8000' //'http://127.0.0.1:8000'
@ -33,6 +37,8 @@ const initialState: RootState = {
error: '', error: '',
data: {} data: {}
}, },
referral: '',
mult: 1
}; };
export const RESET_STATE = 'RESET_STATE'; export const RESET_STATE = 'RESET_STATE';
@ -53,6 +59,16 @@ export const rootReducer: Reducer<RootState> = (state = initialState, action) =>
...state, ...state,
userTg: action.userTg 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:
case ME_REQUEST_SUCCESS: case ME_REQUEST_SUCCESS:
case ME_REQUEST_ERROR: case ME_REQUEST_ERROR:

View 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));
}
}

View File

@ -6,10 +6,9 @@ export const verificationTg = () => {
}; };
let token = ''; let token = '';
console.log(`window.Telegram из verification ${window.Telegram}`);
if(window.Telegram) { if(window.Telegram) {
const tg = window.Telegram.WebApp; const tg = window.Telegram.WebApp;
tg.BackButton.show();
tg.expand(); tg.expand();
tg.setBackgroundColor("#222222"); tg.setBackgroundColor("#222222");
const tgData = tg.initDataUnsafe; const tgData = tg.initDataUnsafe;
@ -41,14 +40,11 @@ export const verificationTg = () => {
token = btoa( token = btoa(
unescape(encodeURIComponent(validation)) unescape(encodeURIComponent(validation))
); );
console.log(`token1 ${token}`);
user.id = tg.initDataUnsafe?.user?.id; user.id = tg.initDataUnsafe?.user?.id;
user.firstName = tg.initDataUnsafe?.user?.first_name; user.firstName = tg.initDataUnsafe?.user?.first_name;
user.lastName = tg.initDataUnsafe?.user?.last_name; user.lastName = tg.initDataUnsafe?.user?.last_name;
} }
console.log(`token2 ${token}`);
//локально //локально
/*token = "TelegramToken"; /*token = "TelegramToken";
user.id = "123456"; user.id = "123456";