({
- path: `/miner/download/referral/${claimCode}`,
- method: 'POST',
- body: {},
- })
- .then((r) => {
- if (r?.success) {
- setError('');
- setClaimCode('');
- onClose();
- }
- setError(r?.message || 'Something went wrong');
- })
- .catch((e) => {
- console.error(e);
- setError('Something went wrong');
- });
- };
-
- const url = `${airdropUrl}/download/${referralCode}`;
- const handleCopy = () => {
- setCopied(true);
- navigator.clipboard.writeText(url);
- };
-
- useEffect(() => {
- if (copied) {
- const timeout = setTimeout(() => {
- setCopied(false);
- }, 2000);
- return () => {
- clearTimeout(timeout);
- };
- }
- }, [copied]);
-
- const claimMarkup = (
- <>
-
-
- , image: }}
- values={{ gems: GIFT_GEMS }}
- />
-
- {t('claimReferralGifts', { gems: GIFT_GEMS })}
-
-
-
- setClaimCode(e.target.value)}
- value={claimCode}
- />
-
-
- {error && {error}}
-
- >
- );
-
- const inviteMarkup = (
- <>
-
-
- , image: }}
- values={{ gems: REFERRAL_GEMS }}
- />
-
- {t('giftReferralCode', { gems: REFERRAL_GEMS })}
-
-
- {url.replace('https://', '')}
-
- {copied ? (
-
- Copied!
-
- ) : (
-
- Copy
-
- )}
-
-
- >
- );
-
- return (
-
-
-
-
-
-
-
-
- {referralCode ? inviteMarkup : claimMarkup}
-
-
-
-
- );
-}
diff --git a/src/containers/Airdrop/AirdropLogin/UserInfo/GemsPill/GemsPill.tsx b/src/containers/Airdrop/AirdropLogin/UserInfo/GemsPill/GemsPill.tsx
deleted file mode 100644
index d80547b1c..000000000
--- a/src/containers/Airdrop/AirdropLogin/UserInfo/GemsPill/GemsPill.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import { StatsPill, StatsNumber, StatsIcon, GemsAnimation, GemImage } from '../styles';
-import gemImage from '../images/gems.png';
-import { AnimatePresence, useSpring } from 'framer-motion';
-import { useEffect, useState } from 'react';
-
-interface Props {
- value: number;
-}
-
-export default function GemsPill({ value }: Props) {
- const [displayValue, setDisplayValue] = useState(0);
- const [animate, setAnimate] = useState(false);
-
- const getRandomX = (() => {
- let lastValue = 20;
- return () => {
- lastValue = lastValue === 20 ? -20 : 20;
- return lastValue;
- };
- })();
-
- const getRandomRotation = (() => {
- let lastValue = 40;
- return () => {
- lastValue = lastValue === 40 ? -40 : 40;
- return lastValue;
- };
- })();
-
- const spring = useSpring(0, { mass: 0.8, stiffness: 50, damping: 20 });
-
- spring.on('change', (latest: number) => {
- setDisplayValue(Math.round(latest));
- });
-
- useEffect(() => {
- spring.set(value);
- }, [spring, value]);
-
- useEffect(() => {
- setAnimate(true);
- const timer = setTimeout(() => setAnimate(false), 1000);
- return () => clearTimeout(timer);
- }, [displayValue]);
-
- return (
-
- {displayValue.toLocaleString()}
-
-
-
-
- {animate && (
-
- {[...Array(8)].map((_, i) => (
-
- ))}
-
- )}
-
-
- );
-}
diff --git a/src/containers/Airdrop/AirdropLogin/UserInfo/GemsPill/styles.ts b/src/containers/Airdrop/AirdropLogin/UserInfo/GemsPill/styles.ts
deleted file mode 100644
index 3c8e36843..000000000
--- a/src/containers/Airdrop/AirdropLogin/UserInfo/GemsPill/styles.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import styled from 'styled-components';
-
-export const Wrapper = styled('div')``;
diff --git a/src/containers/Airdrop/AirdropLogin/UserInfo/UserInfo.tsx b/src/containers/Airdrop/AirdropLogin/UserInfo/UserInfo.tsx
deleted file mode 100644
index da50fc950..000000000
--- a/src/containers/Airdrop/AirdropLogin/UserInfo/UserInfo.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-import {
- Wrapper,
- StatsPill,
- StatsNumber,
- StatsIcon,
- Divider,
- StatsGroup,
- NotificationsButton,
- Dot,
- StyledAvatar,
- Menu,
- MenuItem,
- MenuWrapper,
-} from './styles';
-
-import gemImage from './images/gems.png';
-import { FaBell } from 'react-icons/fa6';
-import { useCallback, useEffect, useState } from 'react';
-import { GIFT_GEMS, REFERRAL_GEMS, useAirdropStore } from '@app/store/useAirdropStore';
-import { useTranslation } from 'react-i18next';
-import { AnimatePresence } from 'framer-motion';
-import DownloadReferralModal from './DownloadReferralModal/DownloadReferralModal';
-import { NumberPill } from '../ConnectButton/styles';
-import GemsPill from './GemsPill/GemsPill';
-
-export default function UserInfo() {
- const { logout, userDetails, airdropTokens, userPoints, wipUI, referralCount } = useAirdropStore();
- const [open, setOpen] = useState(false);
- const [modalOpen, setModalOpen] = useState<'claim' | 'invite' | undefined>(undefined);
-
- const { t } = useTranslation(['airdrop'], { useSuspense: false });
-
- const profileimageurl = userDetails?.user?.profileimageurl;
- const rank = userPoints?.base.rank || userDetails?.user?.rank?.rank;
-
- const handleClick = () => {
- setOpen(!open);
- };
- const handleClose = () => {
- setOpen(false);
- };
-
- const handleLogout = () => {
- logout();
- };
-
- const handleReferalClose = () => {
- setModalOpen(undefined);
- };
-
- const handleClickOutside = useCallback(
- (event: MouseEvent) => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- if (open && (event.target as any)?.id !== 'avatar-wrapper') {
- handleClose();
- }
- },
- [open]
- );
-
- useEffect(() => {
- document.addEventListener('click', handleClickOutside);
- return () => document.removeEventListener('click', handleClickOutside);
- }, [handleClickOutside]);
-
- const [gems, setGems] = useState(0);
-
- useEffect(() => {
- setGems(userPoints?.base.gems || userDetails?.user?.rank?.gems || 0);
- }, [userPoints?.base.gems, userDetails?.user?.rank?.gems]);
-
- if (!wipUI) return null;
- if (!airdropTokens?.token) return null;
-
- const showNotificationButton = false;
-
- return (
- <>
-
-
-
-
- {referralCount?.count ? (
-
- {referralCount.count} 🎁
-
- ) : null}
-
- {rank && (
-
- Rank #{parseInt(rank).toLocaleString()}
-
- )}
-
-
-
-
- {showNotificationButton && (
-
-
-
-
- )}
-
-
-
-
- {open && (
-
- )}
-
-
-
-
- {modalOpen && (
-
- )}
-
- >
- );
-}
diff --git a/src/containers/Airdrop/AirdropLogin/UserInfo/images/avatar.png b/src/containers/Airdrop/AirdropLogin/UserInfo/images/avatar.png
deleted file mode 100644
index 756c7946a..000000000
Binary files a/src/containers/Airdrop/AirdropLogin/UserInfo/images/avatar.png and /dev/null differ
diff --git a/src/containers/Airdrop/AirdropLogin/UserInfo/images/hammers.png b/src/containers/Airdrop/AirdropLogin/UserInfo/images/hammers.png
deleted file mode 100644
index d05772741..000000000
Binary files a/src/containers/Airdrop/AirdropLogin/UserInfo/images/hammers.png and /dev/null differ
diff --git a/src/containers/Airdrop/AirdropLogin/UserInfo/images/shells.png b/src/containers/Airdrop/AirdropLogin/UserInfo/images/shells.png
deleted file mode 100644
index 71dfae951..000000000
Binary files a/src/containers/Airdrop/AirdropLogin/UserInfo/images/shells.png and /dev/null differ
diff --git a/src/containers/Airdrop/AirdropLogin/UserInfo/styles.ts b/src/containers/Airdrop/AirdropLogin/UserInfo/styles.ts
deleted file mode 100644
index da740b026..000000000
--- a/src/containers/Airdrop/AirdropLogin/UserInfo/styles.ts
+++ /dev/null
@@ -1,206 +0,0 @@
-import { m } from 'framer-motion';
-import styled, { css, keyframes } from 'styled-components';
-
-const ring = keyframes`
- 0%, 100% {
- transform: rotate(0deg);
- }
- 25% {
- transform: rotate(15deg);
- }
- 50% {
- transform: rotate(-15deg);
- }
- 75% {
- transform: rotate(15deg);
- }
-`;
-
-export const Wrapper = styled('div')`
- display: flex;
- align-items: center;
- gap: 13px;
-`;
-
-export const StatsGroup = styled('div')`
- display: flex;
- align-items: center;
- gap: 8px;
-`;
-
-export const StatsPill = styled('div')`
- display: flex;
- align-items: center;
- gap: 5px;
-
- border-radius: 20px;
- background-color: #fff;
- box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
-
- height: 36px;
- padding: 0 15px;
- pointer-events: all;
- position: relative;
-`;
-
-export const StatsNumber = styled('div')`
- color: #000;
- font-size: 15px;
- font-weight: 600;
-`;
-
-export const StatsIcon = styled('img')`
- width: 18px;
-`;
-
-export const Divider = styled('div')`
- width: 1px;
- height: 28px;
- background: rgba(0, 0, 0, 0.1);
- flex-shrink: 0;
-`;
-
-export const NotificationsButton = styled('button')`
- background: none;
- border: none;
- cursor: pointer;
- pointer-events: all;
-
- position: relative;
- border-radius: 100%;
- border: 1px solid rgba(0, 0, 0, 0.1);
-
- width: 36px;
- height: 36px;
-
- display: flex;
- align-items: center;
- justify-content: center;
-
- .NotificationsButtonIcon {
- width: 16px;
- height: 16px;
- }
-
- transition: border-color 0.2s ease;
-
- &:hover {
- border-color: rgba(0, 0, 0, 0.3);
-
- .NotificationsButtonIcon {
- animation: ${ring} 0.5s ease-in-out;
- transform-origin: top center;
- }
- }
-`;
-
-export const Dot = styled('div')<{ $color: 'green' | 'red' }>`
- width: 12px;
- height: 12px;
- border-radius: 100%;
-
- position: absolute;
- top: -2px;
- right: -2px;
-
- border: 2px solid #d0d0d0;
-
- background: ${({ $color }) => ($color === 'green' ? '#47D85E' : '#FF0000')};
-`;
-
-export const StyledAvatar = styled('div')<{ $img?: string }>`
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- font-size: 1.25rem;
- line-height: 1;
- border-radius: 50%;
- overflow: hidden;
- user-select: none;
- pointer-events: all;
- cursor: pointer;
- background-color: rgba(0, 0, 0, 0.1);
- width: 36px;
- height: 36px;
- background-size: cover;
- background-position: center;
-
- ${({ $img }) =>
- $img &&
- css`
- background-image: url(${$img});
- `}
-
- box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
-`;
-
-export const Menu = styled(m.div)`
- position: absolute;
- top: 100%;
- right: 0;
- z-index: 2;
-
- border-radius: 10px;
- background: #fff;
- box-shadow: 0px 14px 25px 0px rgba(0, 0, 0, 0.15);
-
- display: flex;
- flex-direction: column;
- align-items: flex-start;
-
- min-width: 205px;
- padding: 10px;
- pointer-events: all;
-
- margin-top: 12px;
-`;
-
-export const MenuItem = styled('div')`
- color: #000;
- font-size: 14px;
- font-style: normal;
- font-weight: 600;
- line-height: normal;
- border-radius: 5px;
-
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 20px;
-
- width: 100%;
- padding: 10px 15px;
- cursor: pointer;
- transition: background-color 0.2s ease;
-
- white-space: nowrap;
-
- &:hover {
- background-color: rgba(0, 0, 0, 0.05);
- }
-
- .StatsIcon-gems {
- width: 12px;
- position: relative;
- z-index: 2;
- }
-`;
-
-export const MenuWrapper = styled('div')`
- position: relative;
-`;
-
-export const GemsAnimation = styled(m.div)`
- position: absolute;
- top: 10px;
- right: 33px;
- z-index: 0;
-`;
-
-export const GemImage = styled(m.img)`
- position: absolute;
- top: 0;
- left: 0;
-`;
diff --git a/src/containers/Airdrop/AirdropLogin/styles.ts b/src/containers/Airdrop/AirdropLogin/styles.ts
deleted file mode 100644
index 82fcccc59..000000000
--- a/src/containers/Airdrop/AirdropLogin/styles.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import styled from 'styled-components';
-
-export const AirdropLoginPosition = styled('div')`
- position: fixed;
- top: 20px;
- right: 20px;
- z-index: 2;
- pointer-events: none;
-`;
diff --git a/src/containers/Airdrop/AirdropPermission/AirdropPermission.tsx b/src/containers/Airdrop/AirdropPermission/AirdropPermission.tsx
index 45ffb59c0..2f34a6981 100644
--- a/src/containers/Airdrop/AirdropPermission/AirdropPermission.tsx
+++ b/src/containers/Airdrop/AirdropPermission/AirdropPermission.tsx
@@ -2,12 +2,11 @@ import { useCallback } from 'react';
import gemImage from './images/gem.png';
import { useTranslation } from 'react-i18next';
import { ToggleSwitch } from '@app/components/elements/ToggleSwitch';
-import { useAirdropStore } from '@app/store/useAirdropStore';
import { useAppConfigStore } from '@app/store/useAppConfigStore';
import { BoxWrapper, Gem1, Gem2, Gem3, Gem4, Position, Text, TextWrapper, Title } from './styles';
export default function AirdropPermission() {
- const wipUI = useAirdropStore((state) => state.wipUI);
+ const airdropUIEnabled = useAppConfigStore((s) => s.airdrop_ui_enabled);
const allowTelemetry = useAppConfigStore((s) => s.allow_telemetry);
const setAllowTelemetry = useAppConfigStore((s) => s.setAllowTelemetry);
const { t } = useTranslation(['airdrop'], { useSuspense: false });
@@ -19,7 +18,7 @@ export default function AirdropPermission() {
return (
- {wipUI && (
+ {airdropUIEnabled && (
<>
@@ -29,8 +28,8 @@ export default function AirdropPermission() {
)}
- {t(wipUI ? 'permission.title' : 'permissionNoGems.title')}
- {t(wipUI ? 'permission.text' : 'permissionNoGems.text')}
+ {t(airdropUIEnabled ? 'permission.title' : 'permissionNoGems.title')}
+ {t(airdropUIEnabled ? 'permission.text' : 'permissionNoGems.text')}
diff --git a/src/containers/Airdrop/Settings/Logout.tsx b/src/containers/Airdrop/Settings/Logout.tsx
new file mode 100644
index 000000000..06adf8175
--- /dev/null
+++ b/src/containers/Airdrop/Settings/Logout.tsx
@@ -0,0 +1,18 @@
+import { Button } from '@app/components/elements/Button';
+import { useAirdropStore } from '@app/store/useAirdropStore';
+import { useAppConfigStore } from '@app/store/useAppConfigStore';
+
+export default function AirdropLogout() {
+ const airdropUIEnabled = useAppConfigStore((s) => s.airdrop_ui_enabled);
+ const logout = useAirdropStore((state) => state.logout);
+ const { userDetails } = useAirdropStore();
+
+ if (!airdropUIEnabled || !userDetails) return null;
+ return (
+
+
+
+ );
+}
diff --git a/src/containers/Airdrop/Settings/ToggleAirdropUi.tsx b/src/containers/Airdrop/Settings/ToggleAirdropUi.tsx
deleted file mode 100644
index bf2c6749a..000000000
--- a/src/containers/Airdrop/Settings/ToggleAirdropUi.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-// import { ToggleSwitch } from '@app/components/elements/ToggleSwitch';
-import { Wrapper } from './styles';
-// import { useAirdropStore } from '@app/store/useAirdropStore';
-// import { useCallback, useRef, useState } from 'react';
-
-export const ToggleAirdropUi = () => {
- // const { wipUI, setWipUI } = useAirdropStore();
- // const [disabled, setDisabled] = useState(!wipUI);
- // const clickCountRef = useRef(0);
-
- // const toggleWipUI = useCallback(() => {
- // if (disabled) {
- // if (clickCountRef.current > 9) {
- // setDisabled(false);
- // } else {
- // clickCountRef.current = clickCountRef.current + 1;
- // return;
- // }
- // }
- // setWipUI(!wipUI);
- // }, [disabled, setWipUI, wipUI]);
-
- return {/* */};
-};
diff --git a/src/containers/Settings/ExperimentalSettings.tsx b/src/containers/Settings/ExperimentalSettings.tsx
index 1e326b7fc..20f2ef214 100644
--- a/src/containers/Settings/ExperimentalSettings.tsx
+++ b/src/containers/Settings/ExperimentalSettings.tsx
@@ -7,7 +7,6 @@ import DebugSettings from '@app/containers/Settings/sections/experimental/DebugS
import AppVersions from '@app/containers/Settings/sections/experimental/AppVersions.tsx';
import VisualMode from '@app/containers/Dashboard/components/VisualMode.tsx';
import { SettingsGroup, SettingsGroupWrapper } from '@app/containers/Settings/components/SettingsGroup.styles.ts';
-import { ToggleAirdropUi } from '@app/containers/Airdrop/Settings/ToggleAirdropUi.tsx';
export const ExperimentalSettings = () => {
const showExperimental = useUIStore((s) => s.showExperimental);
@@ -26,7 +25,6 @@ export const ExperimentalSettings = () => {
-
>
diff --git a/src/containers/Settings/GeneralSettings.tsx b/src/containers/Settings/GeneralSettings.tsx
index 2c2505c48..7e502cebe 100644
--- a/src/containers/Settings/GeneralSettings.tsx
+++ b/src/containers/Settings/GeneralSettings.tsx
@@ -2,6 +2,7 @@ import AirdropPermissionSettings from './sections/general/AirdropPermissionSetti
import LogsSettings from './sections/general/LogsSettings.tsx';
import LanguageSettings from './sections/general/LanguageSettings.tsx';
import { ResetSettingsButton } from './sections/general/ResetSettingsButton.tsx';
+import AirdropLogout from '../Airdrop/Settings/Logout.tsx';
export const GeneralSettings = () => {
return (
@@ -10,6 +11,7 @@ export const GeneralSettings = () => {
+
>
);
};
diff --git a/src/containers/Settings/sections/general/AirdropPermissionSettings.tsx b/src/containers/Settings/sections/general/AirdropPermissionSettings.tsx
index 5e767c8de..f92c37350 100644
--- a/src/containers/Settings/sections/general/AirdropPermissionSettings.tsx
+++ b/src/containers/Settings/sections/general/AirdropPermissionSettings.tsx
@@ -1,6 +1,5 @@
import { useTranslation } from 'react-i18next';
import { ToggleSwitch } from '@app/components/elements/ToggleSwitch';
-import { useAirdropStore } from '@app/store/useAirdropStore';
import { useAppConfigStore } from '@app/store/useAppConfigStore';
import { useCallback } from 'react';
import {
@@ -13,7 +12,7 @@ import {
import { Typography } from '@app/components/elements/Typography.tsx';
export default function AirdropPermissionSettings() {
- const wipUI = useAirdropStore((state) => state.wipUI);
+ const airdropUIEnabled = useAppConfigStore((s) => s.airdrop_ui_enabled);
const allowTelemetry = useAppConfigStore((s) => s.allow_telemetry);
const setAllowTelemetry = useAppConfigStore((s) => s.setAllowTelemetry);
const { t } = useTranslation(['airdrop'], { useSuspense: false });
@@ -27,9 +26,11 @@ export default function AirdropPermissionSettings() {
- {t(wipUI ? 'permission.title' : 'permissionNoGems.title')}
+
+ {t(airdropUIEnabled ? 'permission.title' : 'permissionNoGems.title')}
+
- {t(wipUI ? 'permission.text' : 'permissionNoGems.text')}
+ {t(airdropUIEnabled ? 'permission.text' : 'permissionNoGems.text')}
diff --git a/src/containers/SideBar/SideBar.tsx b/src/containers/SideBar/SideBar.tsx
index a9074e5bb..e280c0a3c 100644
--- a/src/containers/SideBar/SideBar.tsx
+++ b/src/containers/SideBar/SideBar.tsx
@@ -4,6 +4,7 @@ import Heading from './components/Heading';
import { SideBarContainer, SideBarInner, BottomContainer, TopContainer } from './styles';
import MiningButton from '@app/containers/Dashboard/MiningView/components/MiningButton.tsx';
+import AirdropGiftTracker from '@app/containers/Airdrop/AirdropGiftTracker/AirdropGiftTracker';
import { Divider } from '@app/components/elements/Divider.tsx';
import LostConnectionAlert from './components/LostConnectionAlert';
@@ -20,6 +21,7 @@ function SideBar() {
+
diff --git a/src/containers/SideBar/styles.ts b/src/containers/SideBar/styles.ts
index 38afc1428..93c1c280d 100644
--- a/src/containers/SideBar/styles.ts
+++ b/src/containers/SideBar/styles.ts
@@ -42,6 +42,9 @@ export const BottomContainer = styled(m.div)`
justify-self: flex-end;
width: 100%;
padding: 0 16px;
+
+ flex-direction: column;
+ gap: 13px;
`;
// Wallet
diff --git a/src/hooks/airdrop/stateHelpers/useAirdropUserPointsListener.ts b/src/hooks/airdrop/stateHelpers/useAirdropUserPointsListener.ts
index 0e053e80d..f75548a90 100644
--- a/src/hooks/airdrop/stateHelpers/useAirdropUserPointsListener.ts
+++ b/src/hooks/airdrop/stateHelpers/useAirdropUserPointsListener.ts
@@ -4,7 +4,11 @@ import { useEffect } from 'react';
export const useAirdropUserPointsListener = () => {
const setUserPoints = useAirdropStore((state) => state.setUserPoints);
+ const referralCount = useAirdropStore((state) => state.referralCount);
+ const bonusTiers = useAirdropStore((state) => state.bonusTiers);
const setUserPointsReferralCount = useAirdropStore((state) => state.setReferralCount);
+ const setFlareAnimationType = useAirdropStore((state) => state.setFlareAnimationType);
+
useEffect(() => {
let unListen: () => void = () => {
//do nothing
@@ -15,7 +19,20 @@ export const useAirdropUserPointsListener = () => {
const payload = event.payload as UserPoints;
setUserPoints(payload);
if (payload.referralCount) {
- setUserPointsReferralCount(payload.referralCount);
+ if (referralCount?.count !== payload.referralCount.count) {
+ if (referralCount?.count) {
+ setFlareAnimationType('FriendAccepted');
+ if (
+ payload.referralCount.count &&
+ bonusTiers?.find((t) => t.target === payload?.referralCount?.count)
+ ) {
+ setTimeout(() => {
+ setFlareAnimationType('GoalComplete');
+ }, 2000);
+ }
+ }
+ setUserPointsReferralCount(payload.referralCount);
+ }
}
}
})
@@ -27,5 +44,6 @@ export const useAirdropUserPointsListener = () => {
return () => {
unListen();
};
- }, [setUserPoints, setUserPointsReferralCount]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [bonusTiers, referralCount?.count]);
};
diff --git a/src/hooks/airdrop/stateHelpers/useGetAirdropUserDetails.ts b/src/hooks/airdrop/stateHelpers/useGetAirdropUserDetails.ts
index 87cf133c7..815272bab 100644
--- a/src/hooks/airdrop/stateHelpers/useGetAirdropUserDetails.ts
+++ b/src/hooks/airdrop/stateHelpers/useGetAirdropUserDetails.ts
@@ -1,32 +1,9 @@
-import { useAirdropStore, UserEntryPoints, UserDetails, ReferralCount } from '@app/store/useAirdropStore';
+import { useAirdropStore, UserEntryPoints, UserDetails, ReferralCount, BonusTier } from '@app/store/useAirdropStore';
import { useCallback, useEffect } from 'react';
-
-interface RequestProps {
- path: string;
- method: 'GET' | 'POST';
- body?: Record;
-}
-
-export const useAridropRequest = () => {
- const airdropToken = useAirdropStore((state) => state.airdropTokens?.token);
- const baseUrl = useAirdropStore((state) => state.backendInMemoryConfig?.airdropApiUrl);
-
- return async ({ body, method, path }: RequestProps) => {
- if (!baseUrl || !airdropToken) return;
-
- const response = await fetch(`${baseUrl}${path}`, {
- method: method,
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${airdropToken}`,
- },
- body: JSON.stringify(body),
- });
- return response.json() as Promise;
- };
-};
+import { useAridropRequest } from '../utils/useHandleRequest';
export const useGetAirdropUserDetails = () => {
+ const baseUrl = useAirdropStore((state) => state.backendInMemoryConfig?.airdropApiUrl);
const airdropToken = useAirdropStore((state) => state.airdropTokens?.token);
const userDetails = useAirdropStore((state) => state.userDetails);
const setUserDetails = useAirdropStore((state) => state.setUserDetails);
@@ -34,17 +11,24 @@ export const useGetAirdropUserDetails = () => {
const setReferralCount = useAirdropStore((state) => state.setReferralCount);
const setAcceptedReferral = useAirdropStore((state) => state.setAcceptedReferral);
const handleRequest = useAridropRequest();
+ const setBonusTiers = useAirdropStore((state) => state.setBonusTiers);
+ const logout = useAirdropStore((state) => state.logout);
+ // GET USER DETAILS
const fetchUserDetails = useCallback(async () => {
- const data = await handleRequest({
+ return handleRequest({
path: '/user/details',
method: 'GET',
+ onError: logout,
+ }).then((data) => {
+ if (data?.user.id) {
+ setUserDetails(data);
+ return data.user;
+ }
});
- if (!data?.user.id) return;
- setUserDetails(data);
- return data.user;
- }, [handleRequest, setUserDetails]);
+ }, [handleRequest, logout, setUserDetails]);
+ // GET USER POINTS
const fetchUserPoints = useCallback(async () => {
const data = await handleRequest({
path: '/user/score',
@@ -60,6 +44,7 @@ export const useGetAirdropUserDetails = () => {
});
}, [handleRequest, setUserPoints]);
+ // GET USER REFERRAL POINTS
const fetchUserReferralPoints = useCallback(async () => {
const data = await handleRequest<{ count: ReferralCount }>({
path: '/miner/download/referral-count',
@@ -74,29 +59,43 @@ export const useGetAirdropUserDetails = () => {
const fetchAcceptedReferral = useCallback(async () => {
const data = await handleRequest<{ claimed: boolean }>({
- path: '/miner/claimed-referral',
+ path: '/miner/download/claimed-referral',
method: 'GET',
});
setAcceptedReferral(!!data?.claimed);
}, [handleRequest, setAcceptedReferral]);
+ // FETCH BONUS TIERS
+ const fetchBonusTiers = useCallback(async () => {
+ const data = await handleRequest<{ tiers: BonusTier[] }>({
+ path: '/miner/download/bonus-tiers',
+ method: 'GET',
+ });
+ if (!data?.tiers) return;
+ setBonusTiers(data?.tiers);
+ }, [handleRequest, setBonusTiers]);
+
+ // FETCH ALL USER DATA
useEffect(() => {
const fetchData = async () => {
const details = await fetchUserDetails();
- const requests: (() => Promise)[] = [];
+ if (!details) return;
+
+ const requests: Promise[] = [];
if (!details?.rank.gems) {
- requests.push(fetchUserPoints);
+ requests.push(fetchUserPoints());
}
- requests.push(fetchUserReferralPoints);
- requests.push(fetchAcceptedReferral);
+ requests.push(fetchUserReferralPoints());
+ requests.push(fetchAcceptedReferral());
+ requests.push(fetchBonusTiers());
await Promise.all(requests);
};
- if (!userDetails?.user?.id) {
+ if (!userDetails?.user?.id && airdropToken) {
fetchData();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [airdropToken, userDetails?.user?.id]);
+ }, [airdropToken, userDetails, baseUrl]);
};
diff --git a/src/hooks/airdrop/stateHelpers/useGetReferralQuestPoints.ts b/src/hooks/airdrop/stateHelpers/useGetReferralQuestPoints.ts
new file mode 100644
index 000000000..a75928fc6
--- /dev/null
+++ b/src/hooks/airdrop/stateHelpers/useGetReferralQuestPoints.ts
@@ -0,0 +1,57 @@
+import { useEffect } from 'react';
+import { useAridropRequest } from '../utils/useHandleRequest';
+import { useAirdropStore } from '@app/store/useAirdropStore';
+
+export enum QuestNames {
+ MinerReceivedGift = 'miner-received-gift',
+ MinerQuestReferral = 'quest-download-referral',
+}
+
+interface QuestData {
+ displayName: string;
+ isNew: boolean;
+ name: QuestNames;
+ pointTypeName: string;
+ points: number;
+ questFulfilled: boolean;
+ isHidden: boolean;
+}
+
+interface QuestDataResponse {
+ quests: QuestData[];
+}
+
+export const useGetReferralQuestPoints = () => {
+ const handleRequest = useAridropRequest();
+ const { setReferralQuestPoints } = useAirdropStore();
+
+ useEffect(() => {
+ const handleFetch = async () => {
+ const response = await handleRequest({
+ path: `/quest/list-with-fulfillment`,
+ method: 'GET',
+ });
+
+ if (!response?.quests.length) return;
+ const reducedQuest = response.quests.reduce(
+ (acc, quest) => {
+ if (quest.name === QuestNames.MinerReceivedGift) {
+ acc.pointsForClaimingReferral = quest.points;
+ }
+ if (quest.name === QuestNames.MinerQuestReferral) {
+ acc.pointsPerReferral = quest.points;
+ }
+ return acc;
+ },
+ {
+ pointsPerReferral: 0,
+ pointsForClaimingReferral: 0,
+ }
+ );
+ setReferralQuestPoints(reducedQuest);
+ };
+
+ handleFetch();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+};
diff --git a/src/hooks/airdrop/useAirdropSyncState.ts b/src/hooks/airdrop/useAirdropSyncState.ts
index 0160c163b..ea64831c9 100644
--- a/src/hooks/airdrop/useAirdropSyncState.ts
+++ b/src/hooks/airdrop/useAirdropSyncState.ts
@@ -1,6 +1,7 @@
import { useAirdropTokensRefresh } from './stateHelpers/useAirdropTokensRefresh';
import { useAirdropUserPointsListener } from './stateHelpers/useAirdropUserPointsListener';
import { useGetAirdropUserDetails } from './stateHelpers/useGetAirdropUserDetails';
+import { useGetReferralQuestPoints } from './stateHelpers/useGetReferralQuestPoints';
import { useGetRustInMemoryConfig } from './stateHelpers/useGetRustInMemoryConfig';
export const useAirdropSyncState = () => {
@@ -8,4 +9,5 @@ export const useAirdropSyncState = () => {
useAirdropTokensRefresh();
useGetAirdropUserDetails();
useAirdropUserPointsListener();
+ useGetReferralQuestPoints();
};
diff --git a/src/hooks/airdrop/utils/useHandleRequest.ts b/src/hooks/airdrop/utils/useHandleRequest.ts
new file mode 100644
index 000000000..ca17e50e9
--- /dev/null
+++ b/src/hooks/airdrop/utils/useHandleRequest.ts
@@ -0,0 +1,43 @@
+import { useAirdropStore } from '@app/store/useAirdropStore';
+
+interface RequestProps {
+ path: string;
+ method: 'GET' | 'POST';
+ body?: Record;
+ onError?: (e: unknown) => void;
+}
+
+export const useAridropRequest = () => {
+ const airdropToken = useAirdropStore((state) => state.airdropTokens?.token);
+ const baseUrl = useAirdropStore((state) => state.backendInMemoryConfig?.airdropApiUrl);
+
+ return async ({ body, method, path, onError }: RequestProps) => {
+ if (!baseUrl || !airdropToken) return;
+
+ const response = await fetch(`${baseUrl}${path}`, {
+ method: method,
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${airdropToken}`,
+ },
+ body: JSON.stringify(body),
+ });
+
+ try {
+ if (!response.ok) {
+ console.error('Error fetching airdrop data', response);
+ if (onError) {
+ onError(response);
+ }
+ return;
+ }
+ return response.json() as Promise;
+ } catch (e) {
+ console.error('Error fetching airdrop data', e);
+ if (onError) {
+ onError(e);
+ }
+ return;
+ }
+ };
+};
diff --git a/src/store/useAirdropStore.ts b/src/store/useAirdropStore.ts
index a171afee4..61ac451ab 100644
--- a/src/store/useAirdropStore.ts
+++ b/src/store/useAirdropStore.ts
@@ -23,6 +23,13 @@ function parseJwt(token: string): TokenResponse {
}
//////////////////////////////////////////
+//
+
+export interface BonusTier {
+ id: string;
+ target: number;
+ bonusGems: number;
+}
interface TokenResponse {
exp: number;
@@ -101,28 +108,40 @@ export interface BackendInMemoryConfig {
airdropTwitterAuthUrl: string;
}
+type AnimationType = 'GoalComplete' | 'FriendAccepted' | 'BonusGems';
+
+export interface ReferralQuestPoints {
+ pointsPerReferral: number;
+ pointsForClaimingReferral: number;
+}
+
//////////////////////////////////////////
interface AirdropState {
authUuid: string;
- wipUI?: boolean;
acceptedReferral?: boolean;
airdropTokens?: AirdropTokens;
userDetails?: UserDetails;
userPoints?: UserPoints;
referralCount?: ReferralCount;
backendInMemoryConfig?: BackendInMemoryConfig;
+ flareAnimationType?: AnimationType;
+ bonusTiers?: BonusTier[];
+ referralQuestPoints?: ReferralQuestPoints;
}
interface AirdropStore extends AirdropState {
+ setReferralQuestPoints: (referralQuestPoints: ReferralQuestPoints) => void;
+
setAuthUuid: (authUuid: string) => void;
setAirdropTokens: (airdropToken: AirdropTokens) => void;
setUserDetails: (userDetails?: UserDetails) => void;
setUserPoints: (userPoints: UserPoints) => void;
- setWipUI: (wipUI: boolean) => void;
setBackendInMemoryConfig: (config?: BackendInMemoryConfig) => void;
setReferralCount: (referralCount: ReferralCount) => void;
setAcceptedReferral: (acceptedReferral: boolean) => void;
+ setFlareAnimationType: (flareAnimationType?: AnimationType) => void;
+ setBonusTiers: (bonusTiers: BonusTier[]) => void;
logout: () => void;
}
@@ -134,11 +153,14 @@ const clearState: AirdropState = {
userPoints: undefined,
};
+const NOT_PERSISTED_KEYS = ['userPoints', 'backendInMemoryConfig', 'userDetails', 'authUuid', 'referralCount'];
export const useAirdropStore = create()(
persist(
(set) => ({
authUuid: '',
- setWipUI: (wipUI) => set({ wipUI }),
+ setReferralQuestPoints: (referralQuestPoints) => set({ referralQuestPoints }),
+ setFlareAnimationType: (flareAnimationType) => set({ flareAnimationType }),
+ setBonusTiers: (bonusTiers) => set({ bonusTiers }),
setAcceptedReferral: (acceptedReferral) => set({ acceptedReferral }),
logout: () => set(clearState),
setUserDetails: (userDetails) => set({ userDetails }),
@@ -157,9 +179,7 @@ export const useAirdropStore = create()(
{
name: 'airdrop-store',
partialize: (state) =>
- Object.fromEntries(
- Object.entries(state).filter(([key]) => !['userPoints', 'backendInMemoryConfig'].includes(key))
- ),
+ Object.fromEntries(Object.entries(state).filter(([key]) => !NOT_PERSISTED_KEYS.includes(key))),
}
)
);
diff --git a/src/store/useAppConfigStore.ts b/src/store/useAppConfigStore.ts
index 744c0b712..30fe1dab5 100644
--- a/src/store/useAppConfigStore.ts
+++ b/src/store/useAppConfigStore.ts
@@ -34,6 +34,7 @@ const initialState: State = {
monero_address: '',
gpu_mining_enabled: true,
cpu_mining_enabled: true,
+ airdrop_ui_enabled: false,
};
export const useAppConfigStore = create()((set) => ({
diff --git a/src/types/app-status.ts b/src/types/app-status.ts
index 473b8af14..3f40028f4 100644
--- a/src/types/app-status.ts
+++ b/src/types/app-status.ts
@@ -16,6 +16,7 @@ export interface AppConfig {
monero_address: string;
gpu_mining_enabled: boolean;
cpu_mining_enabled: boolean;
+ airdrop_ui_enabled: boolean;
}
export interface CpuMinerMetrics {