diff --git a/src/components/icon/Actions.tsx b/src/components/icon/Actions.tsx index e41b5149a..b0d3f5288 100644 --- a/src/components/icon/Actions.tsx +++ b/src/components/icon/Actions.tsx @@ -163,6 +163,33 @@ export function RemoveLiquidity() { ); } +export function CloseIconWithCircle({ + width, + height, +}: { + width?: string; + height?: string; +}) { + return ( + + + + + + ); +} + export function CloseIcon({ width, height, @@ -218,8 +245,8 @@ export function CloseIconBold() { xmlns="http://www.w3.org/2000/svg" > @@ -741,8 +768,8 @@ function Deposit() { const viewMap = { default: Default, 'Storage Deposit': Deposit, - Swap: Swap, - Withdraw: Withdraw, + Swap, + Withdraw, 'Register Tokens': RegisterToken, 'Add Liquidity': AddLiquidity, 'Remove Liquidity': RemoveLiquidity, @@ -753,7 +780,7 @@ const viewMap = { 'Claim Reward By Farm': AddLiquidity, 'Withdraw Reward': Withdraw, 'Near Deposit': Deposit, - Deposit: Deposit, + Deposit, 'Instant swap': Swap, 'Near Withdraw': Withdraw, 'Add Stable Liquidity': AddLiquidity, diff --git a/src/components/layout/NavigationBar.tsx b/src/components/layout/NavigationBar.tsx index ab0d6e981..899aa7ccc 100644 --- a/src/components/layout/NavigationBar.tsx +++ b/src/components/layout/NavigationBar.tsx @@ -98,6 +98,7 @@ function AccountEntry({ showWalletSelector: boolean; hasBalanceOnRefAccount: boolean; }) { + const isInMemePage = window.location.pathname.includes('meme'); const history = useHistory(); const [hover, setHover] = useState(false); @@ -340,7 +341,7 @@ function AccountEntry({ /> - {(isSignedIn && hover) || showGuider ? ( + {(isSignedIn && hover) || (showGuider && !isInMemePage) ? (
) : null} - {showGuider && !isMobile ? ( + {showGuider && !isMobile && !isInMemePage ? (
+ Stake + xREF to help your favorite MemeToken earn higher farming yields, While + also receiving rewards from the Meme community. +

+ ), + title: 'Vote By xRef', + }, + { + content: ( +

+ Donate + your meme token to stakers of xRef, attracting more xRef holders to + stake their xRef into that MemeToken. +

+ ), + title: 'How show love for voters?', + }, + { + content: ( +

+ Stake + your meme token to help your favorite MemeToken earn higher farming + yields. +

+ ), + title: '', + }, + { + content: ( +

+ Your staked MemeTokens will be displayed here. +

+ ), + title: '', + }, + { + content: ( +

+ Unstaking requires a 5-day wait.Click + Withdraw at the bottom + of the page after this period to reclaim your MemeToken. +

+ ), + title: '', + }, + ]; + + const total = modalContentArray.length; + const [currentStepRefDetail, setCurrentStepRefDetail] = useState( + new Array(5) + ); + const [getRef, setGetRef] = useState(false); + + const [currentPage, setCurrentPage] = useState(1); + + const pageChange = (key) => { + if (key == 'minus' && currentPage > 1) { + setCurrentPage(currentPage - 1); + } else if (key == 'add' && currentPage < total) { + setCurrentPage(currentPage + 1); + } + }; + + useEffect(() => { + // currentStepRefDetail[currentPage - 1].ref.scrollIntoView({ + // behavior: 'smooth', + // }); + window.scrollTo({ + top: currentStepRefDetail[currentPage - 1].y, + behavior: 'smooth', // + }); + }, [currentPage]); + + // provider + const onDataLoaded = (stepRef, index) => { + const obj = { + x: stepRef.getBoundingClientRect().left + window.scrollX, + y: stepRef.getBoundingClientRect().top + window.scrollY, + ref: stepRef, + }; + console.log( + stepRef.getBoundingClientRect().left, + stepRef.getBoundingClientRect().top, + window.scrollX, + window.screenY + ); + // setCurrentPage(index); + const shadowCloneCurrentStepRefDetail = currentStepRefDetail; + shadowCloneCurrentStepRefDetail[index - 1] = obj; + setCurrentStepRefDetail(shadowCloneCurrentStepRefDetail); + setGetRef(true); + + window.scrollTo({ + top: currentStepRefDetail[currentPage - 1].y, + behavior: 'smooth', // + }); + // window.scrollTo( + // currentStepRefDetail[currentPage - 1].x, + // currentStepRefDetail[currentPage - 1].y + // ); + }; + + const intervalRef = useRef(null); + // useEffect(() => { + // // + // intervalRef.current = setInterval(updateModalHeight, 100); // + + // return () => clearInterval(intervalRef.current); + // }, []); + + // const [modalHeight, setModalHeight] = useState('100vh'); // + + // const updateModalHeight = () => { + // const docHeight = Math.max( + // document.body.scrollHeight, + // document.documentElement.scrollHeight, + // document.body.offsetHeight, + // document.documentElement.offsetHeight, + // document.body.clientHeight, + // document.documentElement.clientHeight + // ); + // setModalHeight(`${docHeight}px`); + // }; + + const renderTourContent = () => { + if (!getRef) return null; + return ( + <> +
+ {/* title */} +

+ {modalContentArray[currentPage - 1].title} +

+ {/* main modal content */} +
+ {modalContentArray.map( + (item, index) => + index + 1 == currentPage && ( +
+ {item.content} +
+ ) + )} + {/* pagination */} +
+ {currentPage > 1 && ( + pageChange('minus')} + > + < Pre + + )} + {currentPage < modalContentArray.length && + modalContentArray.length > 1 && ( + pageChange('add')} + > + Next > + + )} +
+ + {/*dashed line */} +
+
+
+
+ {/* close */} +
+ Close + +
+
+ + // + ); + }; + return ( + + {getRef ? ( +
+ {children} +
+ ) : ( + children + )} + {renderTourContent()} +
+ ); +} + +export { BeginerGuideProvider, GuidedTourContext }; diff --git a/src/components/meme/Intro.tsx b/src/components/meme/Intro.tsx new file mode 100644 index 000000000..17c39b45c --- /dev/null +++ b/src/components/meme/Intro.tsx @@ -0,0 +1,211 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { CloseIconWithCircle } from '../../components/icon/Actions'; +import { introCurrentPageStore } from '../../stores/introCurrentPage'; + +function Intro({ + top, + left, + children, +}: { + top: number | string; + left: number | string; + children?: any; +}) { + const { setCurrentPage, currentPage } = introCurrentPageStore() as any; + const modalContentArray = [ + { + content: ( +

+ Stake + xREF to help your favorite MemeToken earn higher farming yields, While + also receiving rewards from the Meme community. +

+ ), + title: 'Vote By xRef', + }, + { + content: ( +

+ Donate + your MemeToken to stakers of xRef, attracting more xRef holders to + stake their xRef into that MemeToken. +

+ ), + title: 'Show Love For Voters', + }, + { + content: ( +

+ Stake + your MemeToken to help your favorite MemeToken earn higher farming + yields. +

+ ), + title: '', + }, + { + content: ( +

+ Your staked MemeTokens will be displayed here. +

+ ), + title: '', + }, + { + content: ( +

+ Unstaking requires a 5-day wait.Click + {`'withdraw'`} at the + bottom of the page after this period to reclaim your MemeToken. +

+ ), + title: '', + }, + ]; + + const total = modalContentArray.length; + + const pageChange = (key) => { + if (key == 'minus' && currentPage > 1) { + setCurrentPage(currentPage - 1); + } else if (key == 'add' && currentPage < total) { + setCurrentPage(currentPage + 1); + } + }; + + const renderTourContent = () => { + return ( + <> +
+
+ {/* title */} +

+ {modalContentArray[currentPage - 1].title} +

+ {/* main modal content */} +
+ {modalContentArray.map( + (item, index) => + index + 1 == currentPage && ( +
+ {item.content} +
+ ) + )} + {/* pagination */} +
+ {currentPage > 1 && ( + pageChange('minus')} + > + < Pre + + )} + {currentPage < modalContentArray.length && + modalContentArray.length > 1 && ( + pageChange('add')} + > + Next > + + )} + {currentPage == modalContentArray.length && ( + { + localStorage.setItem('hasGuided', 'true'); + setCurrentPage(0); + }} + > + got it! + + )} +
+ + {/*dashed line */} +
+
+
+
+ {/* close */} +
{ + localStorage.setItem('hasGuided', 'true'); + setCurrentPage(0); + }} + className="flex justify-end items-center mt-2 mx-3 cursor-pointer text-white text-sm hover:opacity-80" + > + Close + +
+
+
+
+ {children} +
+ + // + ); + }; + return renderTourContent(); +} + +export { Intro }; diff --git a/src/components/meme/MarketSeedsBox.tsx b/src/components/meme/MarketSeedsBox.tsx index 9feae7586..30c3f0bc9 100644 --- a/src/components/meme/MarketSeedsBox.tsx +++ b/src/components/meme/MarketSeedsBox.tsx @@ -1,5 +1,5 @@ -import React, { useState, useContext, useMemo } from 'react'; -import { ArrowRightIcon } from './icons'; +import React, { useState, useContext, useMemo, useEffect } from 'react'; +import { ArrowRightIcon, MemeFinalistToken } from './icons'; import { OprationButton, ConnectToNearBtn } from 'src/components/button/Button'; import { MemeContext } from './context'; import { getMemeDataConfig, getMemeContractConfig } from './memeConfig'; @@ -16,16 +16,22 @@ import YourRewards from './YourRewards'; import Feeders from './Feeders'; import APY from './APY'; import { emptyObject, getSeedApr, isPending, emptyNumber } from './tool'; +import { useScrollToTopOnFirstPage } from '../../state/pool'; +import { introCurrentPageStore } from '../../stores/introCurrentPage'; const is_mobile = isMobile(); const MarketSeedsBox = ({ hidden, displaySeedsPercent, + origin, }: { hidden: boolean; displaySeedsPercent: Record; + origin?: string; }) => { + const { setHasLoaingOver, hasLoaingOver } = introCurrentPageStore() as any; + const { currentPage, hasGuided } = useScrollToTopOnFirstPage(); const { seeds, user_balances, lpSeeds, xrefSeeds, xrefTokenId } = useContext(MemeContext); const { globalState } = useContext(WalletContext); @@ -33,10 +39,15 @@ const MarketSeedsBox = ({ const [isStakeOpen, setIsStakeOpen] = useState(false); const [modal_action_seed_id, set_modal_action_seed_id] = useState(''); const memeDataConfig = getMemeDataConfig(); - const meme_winner_tokens = memeDataConfig.meme_winner_tokens; + // if parent components is intro + const meme_winner_tokens = + origin == 'intro' + ? [memeDataConfig.meme_winner_tokens[0]] + : memeDataConfig.meme_winner_tokens; const { MEME_TOKEN_XREF_MAP } = getMemeContractConfig(); const displaySeeds = useMemo(() => { if (emptyObject(seeds)) return {}; + return meme_winner_tokens.reduce( (acc, memeTokenId) => ({ ...acc, @@ -45,6 +56,13 @@ const MarketSeedsBox = ({ {} ) as Record; }, [meme_winner_tokens, seeds]); + + useEffect(() => { + Object.entries(displaySeeds).length > 0 && + !hasLoaingOver && + setHasLoaingOver(true); + }, [displaySeeds]); + function goFarmDetail(seed_id: string) { const lpSeed = lpSeeds[seed_id]; if (lpSeed.farmList[0].status == 'Ended') { @@ -84,6 +102,9 @@ const MarketSeedsBox = ({ const hasLpSeed = lpSeeds[seed_id]?.farmList[0]?.status && lpSeeds[seed_id]?.farmList[0]?.status !== 'Ended'; + const addBorder = + seed_id === 'token.lonkingnearbackto2024.near' || + seed_id === 'blackdragon.tkn.near'; return (
- + {addBorder ? ( + <> +
+ +
+ + + ) : ( + + )}
{displaySeedsPercent[seed_id]}
@@ -173,20 +211,35 @@ const MarketSeedsBox = ({ isSignedIn ? '' : 'hidden' }`} > - { - set_modal_action_seed_id(seed.seed_id); - setIsStakeOpen(true); - }} - className={`flex flex-grow items-center justify-center text-boxBorder rounded-xl h-12 text-base gotham_bold focus:outline-none xsm:w-full ${ - stakeButtonDisabled - ? 'bg-memePoolBoxBorderColor' - : 'bg-greenLight' - }`} - > - Feed {seed.token_meta_data.symbol} - + {currentPage != 5 ? ( + { + set_modal_action_seed_id(seed.seed_id); + setIsStakeOpen(true); + }} + className={`flex flex-grow items-center justify-center text-boxBorder rounded-xl h-12 text-base gotham_bold focus:outline-none xsm:w-full ${ + stakeButtonDisabled + ? 'bg-memePoolBoxBorderColor' + : 'bg-greenLight' + }`} + > + Feed {seed.token_meta_data.symbol} + + ) : ( + <> + + Unstake + + + Claim + + + )}
); diff --git a/src/components/meme/MemeVoteConfirmModal.tsx b/src/components/meme/MemeVoteConfirmModal.tsx new file mode 100644 index 000000000..9c90293d8 --- /dev/null +++ b/src/components/meme/MemeVoteConfirmModal.tsx @@ -0,0 +1,74 @@ +import React, { useState } from 'react'; +import Modal from 'react-modal'; +import { isMobile } from '../../utils/device'; +import { + OprationButton, + ButtonTextWrapper, +} from 'src/components/button/Button'; +import { ModalCloseIcon } from './icons'; +import { formatSeconds } from './tool'; +function MemeVoteConfirmModal(props: any) { + const { isOpen, onRequestClose, onMemeVote, delay_withdraw_sec } = props; + const [memeVoteLoading, setMemeVoteLoading] = useState(false); + const cardWidth = isMobile() ? '95vw' : '380px'; + function doVote() { + setMemeVoteLoading(true); + onMemeVote(); + } + return ( + +
+
+
+ Feed Instructions +
+ +
+

+ The winner of the next round of MemeSeason has not been decided yet. + Voting for MemeToken at this time might result in wasting{' '} + {formatSeconds(delay_withdraw_sec)} to withdraw it +

+
+ + ( +
+ Got it! +
+ )} + /> +
+
+
+
+ ); +} + +export default MemeVoteConfirmModal; diff --git a/src/components/meme/MemeVoteModal.tsx b/src/components/meme/MemeVoteModal.tsx new file mode 100644 index 000000000..122d8066f --- /dev/null +++ b/src/components/meme/MemeVoteModal.tsx @@ -0,0 +1,293 @@ +import React, { useState, useContext, useMemo, useRef, useEffect } from 'react'; +import Modal from 'react-modal'; +import Big from 'big.js'; +import { isEmpty } from 'lodash'; +import { isMobile } from '../../utils/device'; +import { ModalCloseIcon, SelectsDown, TipIcon } from './icons'; +import { TokenMetadata } from '../../services/ft-contract'; +import { MemeContext } from './context'; +import { getMemeContractConfig } from './memeConfig'; +import { InputAmount } from './InputBox'; +import { toReadableNumber, toNonDivisibleNumber } from '../../utils/numbers'; +import { WalletContext } from '../../utils/wallets-integration'; +import { getMemeUiConfig } from './memeConfig'; +import { stake } from '../../services/meme'; +import MemeVoteConfirmModal from './MemeVoteConfirmModal'; +import { formatSeconds, sortByXrefStaked } from './tool'; +import { + OprationButton, + ButtonTextWrapper, + ConnectToNearBtn, +} from 'src/components/button/Button'; + +const { MEME_TOKEN_XREF_MAP } = getMemeContractConfig(); +const progressConfig = getMemeUiConfig(); +function MemeVoteModal(props: any) { + const { isOpen, onRequestClose } = props; + const [selectedTab, setSelectedTab] = useState(''); + const [amount, setAmount] = useState(''); + const [confirmIsOpen, setConfirmIsOpen] = useState(false); + const { + allTokenMetadatas, + tokenPriceList, + user_balances, + xrefSeeds, + xrefContractConfig, + seeds, + memeContractConfig, + } = useContext(MemeContext); + const { delay_withdraw_sec } = memeContractConfig; + useEffect(() => { + if (!isEmpty(xrefSeeds)) { + setSelectedTab( + Object.keys(MEME_TOKEN_XREF_MAP).sort(sortByXrefStaked(xrefSeeds))[0] + ); + } + }, [xrefSeeds]); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const [selectedTabBalance, seed, FeedIcon] = useMemo(() => { + if (selectedTab && allTokenMetadatas?.[selectedTab]) { + const balance = toReadableNumber( + allTokenMetadatas?.[selectedTab].decimals, + user_balances[selectedTab] || '0' + ); + const FeedIcon = progressConfig?.progress?.[selectedTab]?.feedIcon; + const seed = seeds[selectedTab]; + return [balance, seed, FeedIcon]; + } + return ['0', null, '']; + }, [selectedTab, user_balances, Object.keys(allTokenMetadatas || {}).length]); + const cardWidth = isMobile() ? '100vw' : '28vw'; + const cardHeight = isMobile() ? '90vh' : '80vh'; + const is_mobile = isMobile(); + function stakeToken() { + stake({ + seed, + amount: Big(toNonDivisibleNumber(seed.seed_decimal, amount)).toFixed(0), + }); + } + const disabled = + Big(amount || 0).lte(0) || + Big(amount || 0).gt(selectedTabBalance) || + !selectedTab || + !seed; + const [dropdownVisible, setDropdownVisible] = useState(false); + const selectedDefaultTab = allTokenMetadatas[selectedTab]; + const dropdownRef = useRef(null); + const handleClickOutside = (event) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { + setDropdownVisible(false); + } + }; + useEffect(() => { + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + function openMemeVoteConfirmModal() { + setConfirmIsOpen(true); + } + function closeMemeVoteConfirmModal() { + setConfirmIsOpen(false); + } + if (!selectedTab) return null; + return ( + +
+
+
+
+ Feed For Meme +
+ +
+
+
+ Select Meme you support +
+
+ {Object.keys(MEME_TOKEN_XREF_MAP) + .sort(sortByXrefStaked(xrefSeeds)) + .map((memeTokenId) => { + return ( + setSelectedTab(memeTokenId)} + /> + ); + })} +
+
+
Meme
+
+ + {dropdownVisible && ( +
+ {Object.keys(MEME_TOKEN_XREF_MAP) + .sort(sortByXrefStaked(xrefSeeds)) + .map((memeTokenId, index, array) => ( +
{ + setSelectedTab(memeTokenId); + setDropdownVisible(false); + }} + className={`flex items-center ${ + index !== array.length - 1 ? 'mb-7' : 'mb-4' + }`} + > + +
+ {allTokenMetadatas[memeTokenId]?.symbol} +
+
+ ))} +
+ )} +
+
+
+ {allTokenMetadatas?.[selectedTab] && ( + + )} +
+ {isSignedIn ? ( + +
+ Feed{' '} + {FeedIcon ? ( + + ) : null} +
+
+ ) : ( + + )} +
+ +

+ The unstaked assets will available to be withdrawn in{' '} + {formatSeconds(delay_withdraw_sec)} +

+
+
+
+
+ + {confirmIsOpen && ( + + )} +
+ ); +} + +const Tab = ({ + isSelected, + onSelect, + metadata, +}: { + isSelected: boolean; + onSelect; + metadata: TokenMetadata; +}) => { + const baseStyle = + 'rounded-3xl border border-memeBorderColor pt-2 pl-2 pr-3 pb-2 flex items-center justify-between cursor-pointer outline-none'; + const selectedStyle = 'bg-senderHot text-cardBg'; + const unselectedStyle = 'bg-memeModelgreyColor text-white'; + + return ( + + ); +}; + +export default MemeVoteModal; diff --git a/src/components/meme/MySeedsBox.tsx b/src/components/meme/MySeedsBox.tsx index 2e70a5e3b..e6eb58d79 100644 --- a/src/components/meme/MySeedsBox.tsx +++ b/src/components/meme/MySeedsBox.tsx @@ -1,5 +1,5 @@ import React, { useState, useContext, useMemo } from 'react'; -import { ArrowRightIcon } from './icons'; +import { ArrowRightIcon, MemeFinalistToken } from './icons'; import { OprationButton, ButtonTextWrapper, @@ -163,6 +163,9 @@ const MySeedsBox = ({ memeUnStakeButtonDisabled && xrefUnStakeButtonDisabled; const claimButtonDisabled = memeClaimButtonDisabled && xrefClaimButtonDisabled; + const addBorder = + seed_id === 'token.lonkingnearbackto2024.near' || + seed_id === 'blackdragon.tkn.near'; return (
- + {addBorder ? ( + <> +
+ +
+ + + ) : ( + + )} {Object.keys(displaySeedsPercent).includes(seed_id) ? (
{displaySeedsPercent[seed_id]} diff --git a/src/components/meme/SeedsBox.tsx b/src/components/meme/SeedsBox.tsx index 9789ad1af..848b0be3b 100644 --- a/src/components/meme/SeedsBox.tsx +++ b/src/components/meme/SeedsBox.tsx @@ -1,4 +1,4 @@ -import React, { useState, useContext, useEffect, useMemo } from 'react'; +import React, { useState, useContext, useEffect, useMemo, useRef } from 'react'; import { useHistory } from 'react-router'; import Big from 'big.js'; import { WalletContext } from '../../utils/wallets-integration'; @@ -15,6 +15,19 @@ import { emptyObject, getSeedsTotalStaked } from './tool'; import { MemeContext } from './context'; import { Seed } from '~src/services/farm'; import { formatPercentage } from '../../utils/uiNumber'; +import { Intro } from './Intro'; +import { useScrollToTopOnFirstPage } from '../../state/pool'; +import UserRankingModal from './UserRankingModal'; +import MemeAirdropListForPc from './memeAirdropListForPc'; +import { + CoinMobile, + CoinPc, + MemeRightArrow, + UserRankingMobile, + UserRankingPC, +} from './icons'; +import { isMobile } from '../../utils/device'; +const is_mobile = isMobile(); export interface ITxParams { action: 'stake' | 'unstake'; params: any; @@ -31,6 +44,8 @@ const SeedsBox = () => { const { seeds } = useContext(MemeContext); const memeDataConfig = getMemeDataConfig(); const meme_winner_tokens = memeDataConfig.meme_winner_tokens; + const [isUserRanking, setUserRanking] = useState(false); + const [isShowAirdropModal, setShowAirdropModal] = useState(false); const getURLInfo = () => { const search = window.location.search; const pathname = window.location.pathname; @@ -116,36 +131,119 @@ const SeedsBox = () => { } return displaySeedsPercent; }, [displaySeeds]); + + const positionArray = new Set([3, 4, 5]); + const { currentPage, introRef, hasGuided } = useScrollToTopOnFirstPage(); + const [positionInfo, setPositionInfo] = useState({ top: -140, left: 200 }); + const pagePositions = { + 3: { top: -130, left: 200 }, + 4: { top: -180, left: 250 }, + 5: { top: 150, left: 100 }, + }; + + useEffect(() => { + if (pagePositions[currentPage]) { + setPositionInfo(pagePositions[currentPage]); + } + }, [currentPage]); + return (
-
-
{ - setTab('market'); - }} - > - Feed Meme -
-
{ - setTab('your'); - }} - > - Yours + {/* gudie start */} + {!hasGuided && + positionArray.has(currentPage) && + !is_mobile && + isSignedIn && ( +
+ + {currentPage == 3 && ( +
+
+ )} + {currentPage == 4 && ( +
+
+ Feed Meme +
+
+ Yours +
+
+ )} + {currentPage == 5 && ( + <> +
+
+ Feed Meme +
+
+ Yours +
+
+ +
+ Unstake +
+ + )} +
+
+ )} +
+
+
{ + setTab('market'); + }} + > + Feed Meme +
+
{ + setTab('your'); + }} + > + Yours +
{ + const is_mobile = isMobile(); + const history = useHistory(); + const [isVoteOpen, setIsVoteOpen] = useState(false); + const [isMemeVoteOpen, setIsMemeVoteOpen] = useState(false); + const [isDonateOpen, setIsDonateOpen] = useState(false); + const [isUserRanking, setUserRanking] = useState(false); + const [isShowAirdropModal, setShowAirdropModal] = useState(false); + const [isShowVoteDetailsModal, setVoteDetailsModal] = useState(false); + const { currentPage, introRef, hasGuided } = useScrollToTopOnFirstPage(); + const { globalState } = useContext(WalletContext); + const [showRank, setShowRank] = useState(false); + const isSignedIn = globalState.isSignedIn; + return ( +
+
+
+ Meme staking +
+
+
+ +
+
+
{ + setIsMemeVoteOpen(true); + }} + > + Vote +
+ {/*
setUserRanking(true)} + > + Rank +
*/} +
+
setShowRank(true)} + onMouseLeave={() => setShowRank(false)} + > + Rank +
+ {showRank && ( +
setShowRank(false)} + > +

Coming soon

+
+ )} +
+
setShowAirdropModal(true)} + > + +

Airdrop

+
+
history.push('/airdop')} + > + +

Airdrop

+
+ +
+
+
+
+
+
+
+

+ xREF staking +

+ + Acquire $xREF + + +
+ +

{ + setVoteDetailsModal(true); + }} + > + Detail +

+
+
+
+
+
Current Round:
+
2024/05/04-2024/06/03
+
Next Round:
+
2024/06/06-2024/07/05
+
+
+ +
+
+
+

Current Round:

+
2024/05/04-2024/06/03
+
+
+

Next Round:

+
2024/06/06-2024/07/05
+
+
+ {!hasGuided && + (currentPage === 1 || currentPage === 2) && + !is_mobile && + isSignedIn && ( +
+ +
+
+ Detail +
+
+ Vote +
+
+ Donate +
+
+
+
+ )} +
+
{ + setVoteDetailsModal(true); + }} + > + Detail +
+
{ + setIsVoteOpen(true); + }} + > + Vote +
+
{ + setIsDonateOpen(true); + }} + > + Donate +
+ +
+
+
+ {isVoteOpen ? ( + { + setIsVoteOpen(false); + }} + /> + ) : null} + {isMemeVoteOpen ? ( + { + setIsMemeVoteOpen(false); + }} + /> + ) : null} + {isDonateOpen ? ( + { + setIsDonateOpen(false); + }} + /> + ) : null} + {isUserRanking ? ( + { + setUserRanking(false); + }} + /> + ) : null} + {isShowAirdropModal ? ( + { + setShowAirdropModal(false); + }} + /> + ) : null} + {isShowVoteDetailsModal ? ( + { + setVoteDetailsModal(false); + }} + /> + ) : null} +
+ ); +}; +export default Staking; diff --git a/src/components/meme/StakingChart.tsx b/src/components/meme/StakingChart.tsx new file mode 100644 index 000000000..bce169e62 --- /dev/null +++ b/src/components/meme/StakingChart.tsx @@ -0,0 +1,239 @@ +import React, { useContext, useMemo, useState, useEffect } from 'react'; +import PropTypes from 'prop-types'; +import { PieChart, Pie, Sector, Cell } from 'recharts'; +import Big from 'big.js'; +import { MemeContext } from './context'; +import { getMemeDataConfig, getMemeContractConfig } from './memeConfig'; +import { emptyObject } from './tool'; +import { toReadableNumber } from '../../utils/numbers'; +import { isMobile } from 'src/utils/device'; +import { + formatPercentage, + toInternationalCurrencySystem_number, + toInternationalCurrencySystem_usd, +} from '../../utils/uiNumber'; +import { Seed } from '../..//services/farm'; + +const is_mobile = isMobile(); +const renderActiveShape = (props) => { + const { cx, cy, innerRadius, outerRadius, startAngle, endAngle, fill } = + props; + + return ( + + + + ); +}; + +const StakingChart = ({ chartType }) => { + const [activeIndex, setActiveIndex] = useState(0); + const { seeds, xrefSeeds, allTokenMetadatas } = useContext(MemeContext) || {}; + const { MEME_TOKEN_XREF_MAP } = getMemeContractConfig(); + const memeDataConfig = getMemeDataConfig(); + const meme_winner_tokens = memeDataConfig.meme_winner_tokens; + + const displaySeeds = useMemo(() => { + if (emptyObject(seeds)) return {}; + return meme_winner_tokens.reduce( + (acc, memeTokenId) => ({ + ...acc, + ...{ [memeTokenId]: seeds[memeTokenId] }, + }), + {} + ) as Record; + }, [meme_winner_tokens, seeds]); + + useEffect(() => { + if (!seeds) { + console.error('Error: seeds is null'); + } + if (!xrefSeeds) { + console.error('Error: xrefSeeds is null'); + } + }, [seeds, xrefSeeds]); + + const chartData = useMemo(() => { + if ( + emptyObject(displaySeeds) || + emptyObject(xrefSeeds) || + !allTokenMetadatas + ) { + return []; + } + + const { pie_color } = getMemeDataConfig(); + let totalTvl = Big(0); + let totalTvlForCalculation = chartType === 'meme' ? Big(0) : Big(0); + const dataItems = []; + + Object.keys(displaySeeds).forEach((seed_id) => { + const seed = displaySeeds[seed_id]; + const xrefSeed = xrefSeeds[MEME_TOKEN_XREF_MAP[seed_id]]; + if (seed && xrefSeed) { + const seedTvl = Big(seed.seedTvl); + const seedAmount = toReadableNumber( + seed.seed_decimal, + seed.total_seed_amount + ); + const xrefSeedTvl = Big(xrefSeed.seedTvl); + const xrefSeedAmount = toReadableNumber( + xrefSeed.seed_decimal, + xrefSeed.total_seed_amount + ); + + if (chartType === 'meme') { + totalTvl = totalTvl.plus(seedTvl); + totalTvlForCalculation = totalTvl; + } else if (chartType === 'xref') { + totalTvl = totalTvl.plus(xrefSeedTvl); + totalTvlForCalculation = totalTvl; + } + + dataItems.push({ + seed_id, + seedTvl: seedTvl.toFixed(), + xrefSeedTvl: xrefSeedTvl.toFixed(), + amount: chartType === 'meme' ? Big(seedAmount) : Big(xrefSeedAmount), + symbol: allTokenMetadatas[seed_id]?.symbol, + icon: allTokenMetadatas[seed_id]?.icon, + color: pie_color[seed_id], + }); + } + }); + + return dataItems + .map((item) => { + const value = + chartType === 'meme' ? Big(item.seedTvl) : Big(item.xrefSeedTvl); + const percent = totalTvlForCalculation.gt(0) + ? value.div(totalTvlForCalculation).mul(100).toFixed(2) + : 0; + return { + ...item, + value: Number(percent), + percent, + }; + }) + .sort((a, b) => b.value - a.value); + }, [displaySeeds, xrefSeeds, allTokenMetadatas, chartType]); + + const onPieEnter = (_, index) => { + setActiveIndex(index); + }; + + const onPieLeave = () => { + setActiveIndex(0); + }; + + const renderCenteredText = (data, activeIndex) => { + if (activeIndex < 0 || activeIndex >= data.length) return null; + + const activeEntry = data[activeIndex]; + + return ( + <> + + + {activeEntry.symbol} + + + {activeEntry.percent}% + + + {chartType === 'xref' + ? toInternationalCurrencySystem_number(activeEntry.amount) + ' xREF' + : toInternationalCurrencySystem_number(activeEntry.amount)} + + + ); + }; + + const customActiveShape = (props) => { + // eslint-disable-next-line react/prop-types + if (props.index === activeIndex) { + return renderActiveShape({ + ...props, + // eslint-disable-next-line react/prop-types + outerRadius: props.outerRadius + 5, + }); + } + return renderActiveShape(props); + }; + + if (chartData.length === 0) return null; + + return ( +
+ + + {chartData.map((entry, index) => ( + + ))} + + {activeIndex !== -1 && renderCenteredText(chartData, activeIndex)} + +
+ ); +}; + +StakingChart.propTypes = { + chartType: PropTypes.oneOf(['meme', 'xref']).isRequired, +}; + +export default StakingChart; diff --git a/src/components/meme/UserRankingModal.tsx b/src/components/meme/UserRankingModal.tsx new file mode 100644 index 000000000..3174eb050 --- /dev/null +++ b/src/components/meme/UserRankingModal.tsx @@ -0,0 +1,857 @@ +import React, { useContext, useEffect, useRef, useState } from 'react'; +import Modal from 'react-modal'; +import { isMobile } from '../../utils/device'; +import { + MemeEllipsis, + ModalCloseIcon, + UserStakeRankingFirst, + UserStakeRankingLast, + UserStakeRankingMobileTab1, + UserStakeRankingMobileTab2, + UserStakeRankingMobileTab3, + UserStakeRankingNext, + UserStakeRankingPopupDown, + UserStakeRankingPrevious, + UserStakeRankingSort, + UserStakeRankingTab1, + UserStakeRankingTab2, + UserStakeRankingTab3, +} from './icons'; +import { MemeContext } from './context'; +import { + getMemeFarmingAssetsList, + getMemeFarmingTokens, + getMemeFarmingTotalAssetsList, +} from '../../services/api'; +import Loading from '../layout/Loading'; +function UserRankingModal(props: any) { + const { isOpen, onRequestClose } = props; + const cardWidth = isMobile() ? '90vw' : '52vw'; + const cardHeight = isMobile() ? '588px' : 'auto'; + const is_mobile = isMobile(); + const { allTokenMetadatas } = useContext(MemeContext); + const [isLoading, setIsLoading] = useState(true); + const dropdownRef = useRef(null); + const [memeFarmingTokens, setMemeFarmingTokens] = useState([]); + const [isOpenToken, setIsOpenToken] = useState(false); + const [selectedToken, setSelectedToken] = useState('All'); + const [tableDate, setTableDate] = useState([]); + const [currentPage, setCurrentPage] = useState(1); + const [itemsPerPage, setItemsPerPage] = useState(isMobile() ? 5 : 7); + const [totalPages, setTotalPages] = useState(0); + const rowRefs = useRef([]); + const [hoveredRow, setHoveredRow] = useState(null); + const [sorted, setSorted] = useState(false); + const [currentPageInput, setCurrentPageInput] = useState(''); + useEffect(() => { + if (isOpen) { + getMemeFarmingTokens() + .then((data) => { + setMemeFarmingTokens(data?.data); + }) + .catch((error) => { + console.log(error); + setMemeFarmingTokens([]); + }); + } + }, [isOpen]); + useEffect(() => { + let loadingTimeout = null; + if (isOpen) { + const fetchData = async () => { + loadingTimeout = setTimeout(() => { + setIsLoading(true); + }, 500); + try { + let data; + const offset = (currentPage - 1) * itemsPerPage; + if (selectedToken === 'All') { + data = await getMemeFarmingTotalAssetsList( + itemsPerPage, + offset, + 'desc' + ); + } else { + data = await getMemeFarmingAssetsList( + selectedToken, + 'total_value', + itemsPerPage, + offset, + 'desc' + ); + } + setTableDate(data?.data.list || []); + setTotalPages(Math.ceil(data?.data.total / itemsPerPage)); + setIsLoading(false); + } catch (error) { + console.log(error); + setTableDate([]); + } finally { + clearTimeout(loadingTimeout); + setIsLoading(false); + } + }; + fetchData(); + } + return () => clearTimeout(loadingTimeout); + }, [isOpen, selectedToken, currentPage, itemsPerPage]); + useEffect(() => { + setCurrentPage(1); + }, [selectedToken]); + useEffect(() => { + const checkIfClickedOutside = (e) => { + if ( + isOpen && + dropdownRef.current && + !dropdownRef.current.contains(e.target) + ) { + setIsOpenToken(false); + } + }; + + document.addEventListener('mousedown', checkIfClickedOutside); + + return () => { + document.removeEventListener('mousedown', checkIfClickedOutside); + }; + }, [isOpen]); + const handleSelectToken = (token) => { + setSelectedToken(token); + setIsOpenToken(false); + setSorted(false); + }; + const paginate = (pageNumber) => { + setCurrentPage(pageNumber); + }; + const getCustomPaginationRange = (current, total) => { + if (is_mobile) { + const pages = []; + if (total <= 3) { + return Array.from({ length: total }, (_, index) => index + 1); + } else if (current === 1) { + pages.push(`${current}/${total}`); + } else if (current === total) { + pages.push(`${total}`); + } else { + pages.push(`${current}/${total}`); + } + return pages; + } else { + let pages = []; + const lastPage = total; + const secondLastPage = total - 1; + if (total <= 5) { + return Array.from({ length: total }, (_, index) => index + 1); + } + if (current === 1) { + pages = [1, 2, 3, 4, '...', lastPage]; + } else if (current === 2) { + pages = [1, 2, 3, 4, '...', lastPage]; + } else if (current === lastPage - 3) { + pages = [current - 1, current, lastPage - 2, lastPage - 1, lastPage]; + } else if (current === lastPage - 2) { + pages = [current - 2, current - 1, current, lastPage - 1, lastPage]; + } else if (current === lastPage - 1) { + pages = [current - 3, current - 2, current - 1, current, lastPage]; + } else if (current === lastPage) { + pages = [ + lastPage - 4, + lastPage - 3, + lastPage - 2, + lastPage - 1, + lastPage, + ]; + } else { + pages = [ + current - 1, + current, + current + 1, + current + 2, + '...', + lastPage, + ]; + } + return pages; + } + }; + function abbreviateNumber(value) { + const number = Number(value); + if (isNaN(number)) { + return ''; + } else if (number >= 1e9) { + return (number / 1e9).toFixed(2) + 'B'; + } else if (number >= 1e6) { + return (number / 1e6).toFixed(2) + 'M'; + } else if (number >= 1e3) { + return (number / 1e3).toFixed(2) + 'K'; + } else { + return number.toFixed(2); + } + } + const handleMouseEnterRow = (itemIndex) => { + setHoveredRow(itemIndex); + }; + + const handleMouseLeaveRow = () => { + setHoveredRow(null); + }; + const sortTableData = () => { + const sortedData = [...tableDate]; + if (selectedToken === 'All') { + sortedData.sort((a, b) => { + const totalA = a.token_list.reduce((acc, curr) => acc + curr.total, 0); + const totalB = b.token_list.reduce((acc, curr) => acc + curr.total, 0); + return totalB - totalA; + }); + } else { + sortedData.sort((a, b) => b.total - a.total); + } + setTableDate(sortedData); + setSorted(true); + setCurrentPage(1); + }; + const getPopupPosition = (triggerElement) => { + if (triggerElement) { + const rect = triggerElement.getBoundingClientRect(); + const windowHeight = window.innerHeight; + const popupHeight = 200; + if (windowHeight - rect.bottom < popupHeight) { + return { top: 'initial', bottom: '150%' }; + } else { + return { top: '150%', bottom: 'initial' }; + } + } + return null; + }; + const handlePageJump = () => { + const pageNumber = parseInt(currentPageInput); + if (!isNaN(pageNumber) && pageNumber > 0 && pageNumber <= totalPages) { + setCurrentPage(pageNumber); + setCurrentPageInput(''); + } + }; + return ( + +
+
+
+ User stake ranking +
+ +
+
+
+ Ranking +
+
+ Wallet +
+
+ Total Staked Value +
+
+
+
setIsOpenToken(!isOpenToken)} + > + {allTokenMetadatas[selectedToken]?.icon ? ( + + ) : ( + All + )} + + {allTokenMetadatas[selectedToken]?.symbol || 'All'} + + +
+ {/* */} +
+ {isOpenToken && ( +
+
handleSelectToken('All')} + > +
All
+
+ {memeFarmingTokens && + memeFarmingTokens.map((token, index) => ( +
handleSelectToken(token)} + > +
+ + {allTokenMetadatas[token]?.symbol} +
+
+ ))} +
+ )} +
+
+ {isLoading ? ( +
+ +
+ ) : ( + <> +
+
+ {tableDate && + tableDate.map((item, index) => ( +
+
+ {(() => { + const globalIndex = + index + 1 + (currentPage - 1) * itemsPerPage; + return globalIndex === 1 ? ( + + ) : globalIndex === 2 ? ( + + ) : globalIndex === 3 ? ( + + ) : ( + globalIndex + ); + })()} +
+
+ {item.wallet} +
+
+ {item.total_value < 0.01 + ? '$<0.01' + : `$${abbreviateNumber(item.total_value)}`} +
+
+ {selectedToken === 'All' && + Array.isArray(item.token_list) ? ( +
handleMouseEnterRow(index)} + onMouseLeave={handleMouseLeaveRow} + > + {item.token_list.map((token, tokenIndex) => ( +
+ +
+ ))} + {hoveredRow === index && ( +
+ {item.token_list.map((token) => ( +
+
+ +

+ {allTokenMetadatas[token.token] + ?.symbol || ''} +

+
+
+ {token.total < 0.01 + ? '<0.01' + : `${abbreviateNumber(token.total)}`} +
+
+ ))} +
+ )} +
+ ) : ( +
+ {item.total < 0.01 + ? '<0.01' + : `${abbreviateNumber(item.total)}`} +
+ )} +
+
+ ))} +
+
+
+ {tableDate && + tableDate.map((item, index) => ( +
+
+ {(() => { + const globalIndex = + index + 1 + (currentPage - 1) * itemsPerPage; + return globalIndex === 1 ? ( + + ) : globalIndex === 2 ? ( + + ) : globalIndex === 3 ? ( + + ) : ( + globalIndex + ); + })()} +
+
+
+
+ {item.total_value < 0.01 + ? '$<0.01' + : `$${abbreviateNumber(item.total_value)}`} +
+
+ {selectedToken === 'All' && + Array.isArray(item.token_list) ? ( +
(rowRefs.current[index] = el)} + onMouseEnter={() => handleMouseEnterRow(index)} + onMouseLeave={handleMouseLeaveRow} + > + {item.token_list.length >= 4 ? ( + <> + {item.token_list + .slice(0, 2) + .map((token, tokenIndex) => ( +
+ +
+ ))} +
+ +
+
+ +
+ + ) : ( + item.token_list.map((token, tokenIndex) => ( +
+ +
+ )) + )} + + {hoveredRow === index && ( +
+ {item.token_list.map((token) => ( +
+
+ +

+ {allTokenMetadatas[token.token] + ?.symbol || ''} +

+
+
+ {token.total < 0.01 + ? '<0.01' + : `${abbreviateNumber(token.total)}`} +
+
+ ))} +
+ )} +
+ ) : ( +
+ {item.total < 0.01 + ? '<0.01' + : `${abbreviateNumber(item.total)}`} +
+ )} +
+
+
+ {item.wallet} +
+
+
+ ))} +
+
+
+
{ + if (currentPage > 1) paginate(1); + }} + > +
+ +
+

First

+
+ +
1 + ? 'text-white cursor-pointer' + : 'text-primaryText' + }`} + onClick={() => { + if (currentPage > 1) { + paginate(currentPage - 1); + } + }} + > +
1 + ? 'border-senderHot' + : 'border-memeUserStackeMobileBgColor' + } ${is_mobile ? 'border' : ''}`} + > + 1 + ? '#00FFD1' + : '#7E8A93' + : currentPage > 1 + ? '#FFFFFF' + : '#7E8A93' + } + /> +
+

Previous

+
+
+
+
+ +
+
+

+ Go to +

+ setCurrentPageInput(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + handlePageJump(); + } + }} + /> +
+
+
+
{ + if (currentPage < totalPages) { + paginate(currentPage + 1); + } + }} + > +

Next

+
+ +
+
+
{ + if (currentPage < totalPages) { + paginate(totalPages); + } + }} + > +

Last

+
+ +
+
+
+
+ + )} +
+
+ ); +} + +interface PaginationComponentProps { + getCustomPaginationRange: ( + currentPage: number, + totalPages: number + ) => Array; + currentPage: number; + totalPages: number; + paginate: (pageNumber: number) => void; +} +const PaginationComponent: React.FC = ({ + getCustomPaginationRange, + currentPage, + totalPages, + paginate, +}) => { + const paginationRange = getCustomPaginationRange(currentPage, totalPages); + return ( + <> + {paginationRange && + paginationRange.map((page, index) => { + if (typeof page === 'number') { + return ( + + ); + } else { + return ( + + {page} + + ); + } + })} + + ); +}; +export default UserRankingModal; diff --git a/src/components/meme/VoteChart.tsx b/src/components/meme/VoteChart.tsx index 70bdedfb9..0660c0d35 100644 --- a/src/components/meme/VoteChart.tsx +++ b/src/components/meme/VoteChart.tsx @@ -28,6 +28,7 @@ const renderActiveShape = (props) => { endAngle={endAngle} fill={fill} stroke="transparent" + strokeWidth={0} /> ); @@ -85,28 +86,27 @@ const MyPieChart = () => { return ( <> - + {activeEntry.symbol} @@ -114,10 +114,10 @@ const MyPieChart = () => { {toInternationalCurrencySystem_number(activeEntry.value)} xREF @@ -137,12 +137,12 @@ const MyPieChart = () => { return renderActiveShape(props); }; - const sectorStrokeWidth = 3; - const sectorStrokeColor = '#21232F'; + const sectorStrokeWidth = 2; + const sectorStrokeColor = '#00121f'; if (!chartData) return null; return (
- + { nameKey="name" cx="50%" cy="50%" - innerRadius={is_mobile ? 115 : 130} - outerRadius={is_mobile ? 150 : 172.5} + innerRadius={is_mobile ? 130 : 130} + outerRadius={is_mobile ? 156 : 156} onMouseEnter={onPieEnter} onMouseLeave={onPieLeave} stroke={sectorStrokeColor} strokeWidth={sectorStrokeWidth} + paddingAngle={1} > {chartData.map((entry, index) => ( { + if (!emptyObject(xrefSeeds) && !emptyObject(allTokenMetadatas)) { + getDonateList(); + } + }, [xrefSeeds, allTokenMetadatas, tokenPriceList]); + async function getDonateList() { + const { MEME_TOKEN_XREF_MAP } = getMemeContractConfig(); + const { meme_winner_tokens } = getMemeDataConfig(); + const list: IDonate[] = Object.entries(MEME_TOKEN_XREF_MAP).reduce( + (acc, [memeTokenId, xrefContractId]) => { + const xrefSeed = xrefSeeds[xrefContractId]; + const totalMemeReward = toReadableNumber( + allTokenMetadatas?.[memeTokenId]?.decimals || 0, + getTotalRewardBalance(xrefSeed, donateBalances[memeTokenId]) + ); + + acc.push({ + memeTokenId, + donateBalance: totalMemeReward, + donateValue: Big(totalMemeReward) + .mul(tokenPriceList[memeTokenId]?.price || '0') + .toFixed(), + metadata: allTokenMetadatas[memeTokenId], + xrefStakedAmount: toReadableNumber( + allTokenMetadatas[xrefTokenId]?.decimals || 0, + xrefSeed.total_seed_amount + ), + xrefVoters: xrefSeed.farmer_count, + win: meme_winner_tokens.includes(memeTokenId), + }); + return acc; + }, + [] + ); + const winners = meme_winner_tokens.reduce((acc, memeTokenId, index) => { + return { + ...acc, + ...{ [memeTokenId]: meme_winner_tokens.length - index }, + }; + }, {}); + list.sort((b, a) => { + return (winners[a.memeTokenId] || 0) - (winners[b.memeTokenId] || 0); + }); + setDonateList(list); + } + return ( + +
+
+
Detail
+ +
+
+ +
+
+
+ ); +} +function DonateList({ donateList }: { donateList: IDonate[] }) { + return ( + <> + {is_mobile ? ( + + ) : ( + + )} + + ); +} +function DonateListPc({ donateList }: { donateList: IDonate[] }) { + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const { xrefSeeds, xrefTokenId, xrefFarmContractUserData } = + useContext(MemeContext); + return ( +
+
+
+
+
+
Meme Project
+
xREF
+
Voters
+
Donation
+ {isSignedIn ?
You Voted
: null} +
+ {donateList + .sort((b, a) => { + return Big(a.xrefStakedAmount) + .minus(b.xrefStakedAmount) + .toNumber(); + }) + .map((donateData: IDonate) => { + const xrefContractId = + MEME_TOKEN_XREF_MAP[donateData.memeTokenId]; + const xrefSeed: Seed = xrefSeeds[xrefContractId]; + const userStakedAmount = toReadableNumber( + xrefSeed.seed_decimal, + xrefFarmContractUserData?.[xrefContractId]?.join_seeds?.[ + xrefTokenId + ]?.free_amount || '0' + ); + return ( +
+
+
+ + {donateData.metadata?.symbol} + {donateData.win ? ( +
+ Listed +
+ ) : null} +
+
+
+ {toInternationalCurrencySystem_number( + donateData.xrefStakedAmount + )} +
+
{donateData.xrefVoters}
+
+ +
+ + {toInternationalCurrencySystem_number( + donateData.donateBalance + )} + + + {toInternationalCurrencySystem_usd( + donateData.donateValue + )} + +
+
+ {isSignedIn ? ( +
+
+ {toInternationalCurrencySystem_number( + userStakedAmount + )} +
+
+ ) : null} +
+ ); + })} +
+
+
+
+ ); +} +function DonateListMobile({ donateList }: { donateList: IDonate[] }) { + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const { xrefSeeds, xrefTokenId, xrefFarmContractUserData } = + useContext(MemeContext); + return ( +
+
+ {donateList + .sort((b, a) => { + return Big(a.xrefStakedAmount).minus(b.xrefStakedAmount).toNumber(); + }) + .map((donateData: IDonate) => { + const xrefContractId = MEME_TOKEN_XREF_MAP[donateData.memeTokenId]; + const xrefSeed: Seed = xrefSeeds[xrefContractId]; + const userStakedAmount = toReadableNumber( + xrefSeed.seed_decimal, + xrefFarmContractUserData?.[xrefContractId]?.join_seeds?.[ + xrefTokenId + ]?.free_amount || '0' + ); + return ( +
+
+ + + {donateData.metadata?.symbol} + + {donateData.win ? ( +
+ Listed +
+ ) : null} +
+
+ xREF + + {toInternationalCurrencySystem_number( + donateData.xrefStakedAmount + )} + +
+
+ Voters + + {donateData.xrefVoters} + +
+
+ Donation +
+ + {' '} + {toInternationalCurrencySystem_number( + donateData.donateBalance + )} + + + {' '} + {toInternationalCurrencySystem_usd( + donateData.donateValue + )} + +
+
+ {isSignedIn ? ( +
+ You Voted + + {toInternationalCurrencySystem_number(userStakedAmount)} + +
+ ) : null} +
+ ); + })} +
+
+ ); +} +export default VoteDetailsModal; diff --git a/src/components/meme/VoteSheet.tsx b/src/components/meme/VoteSheet.tsx index 6320c8fc4..d5cdd8f18 100644 --- a/src/components/meme/VoteSheet.tsx +++ b/src/components/meme/VoteSheet.tsx @@ -1,4 +1,4 @@ -import React, { useState, useContext, useMemo } from 'react'; +import React, { useState, useContext, useMemo, useRef, useEffect } from 'react'; import Big from 'big.js'; import { isMobile } from '../../utils/device'; import { ArrowRightTopIcon } from './icons'; @@ -44,6 +44,7 @@ function VoteSheet({ hidden }: { hidden: boolean }) { return sum.plus(seedTotalStakedAmount); }, new Big(0)); }, [xrefSeeds]); + return (
diff --git a/src/components/meme/VoteXREF.tsx b/src/components/meme/VoteXREF.tsx index d43cc134f..920aae199 100644 --- a/src/components/meme/VoteXREF.tsx +++ b/src/components/meme/VoteXREF.tsx @@ -1,8 +1,7 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import VoteSheet from './VoteSheet'; import VotersSheet from './VotersSheet'; import { isMobile } from '../../utils/device'; - const VoteXREF = () => { const [activeTab, setActiveTab] = useState('vote'); const is_mobile = isMobile(); diff --git a/src/components/meme/countdown.tsx b/src/components/meme/countdown.tsx index 3addbb345..71ef22f68 100644 --- a/src/components/meme/countdown.tsx +++ b/src/components/meme/countdown.tsx @@ -20,7 +20,9 @@ const Countdown = ({ onCountdownFinish }) => { const [countdownFinished, setCountdownFinished] = useState(false); useEffect(() => { const targetDate = new Date(Date.UTC(new Date().getUTCFullYear(), 5, 1)); - const endDate = new Date(Date.UTC(new Date().getUTCFullYear(), 5, 6)); + const endDate = new Date( + Date.UTC(new Date().getUTCFullYear(), 5, 5, 0, 0, 0) + ); const updateCountdown = () => { const nowUtc = Date.now(); if (nowUtc >= endDate.getTime()) { diff --git a/src/components/meme/icons.tsx b/src/components/meme/icons.tsx index ec95ccc06..094b486be 100644 --- a/src/components/meme/icons.tsx +++ b/src/components/meme/icons.tsx @@ -1224,9 +1224,9 @@ export function CountdownRightBottomMobileBg(props: any) { export function CountdownLeftBg(props: any) { return ( ); } @@ -1313,3 +1313,1769 @@ export function CountdownFinishMobile(props: any) { /> ); } + +export function CoinPc() { + return ( + + + + + + + + + + + + + + + ); +} + +export function CoinMobile() { + return ( + + + + + + + + + + + + + + + ); +} + +export function RuleTips() { + return ( + + + + + + ); +} + +export function RuleIcon() { + return ( + + + + + + ); +} + +export function TriangleUp() { + return ( + + + + ); +} + +export function TriangleDown() { + return ( + + + + ); +} + +export function UserRankingPC() { + return ( + + + + + + + + + + + + + + + + + + ); +} + +export function UserRankingMobile() { + return ( + + + + + + + + + + + + + + + + + + ); +} + +export function UserStakeRankingSort(props: any) { + return ( + + + + ); +} + +export function UserStakeRankingTab1(props: any) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} +export function UserStakeRankingTab2(props: any) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} +export function UserStakeRankingTab3(props: any) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export function UserStakeRankingMobileTab1(props: any) { + return ( + + ); +} +export function UserStakeRankingMobileTab2(props: any) { + return ( + + ); +} +export function UserStakeRankingMobileTab3(props: any) { + return ( + + ); +} + +export function UserStakeRankingPrevious({ + color, + ...props +}: { color: string } & React.SVGProps) { + return ( + + + + ); +} +export function UserStakeRankingNext({ + color, + ...props +}: { color: string } & React.SVGProps) { + return ( + + + + ); +} +export function UserStakeRankingFirst({ + color, + ...props +}: { color: string } & React.SVGProps) { + return ( + + + + ); +} +export function UserStakeRankingLast({ + color, + ...props +}: { color: string } & React.SVGProps) { + return ( + + + + ); +} +export function UserStakeRankingPopupDown({ ...props }) { + return ( + + + + ); +} + +export function Goback() { + return ( + + + + ); +} + +export function MemeRightArrow() { + return ( + + + + ); +} + +export function MemeEllipsis() { + return ( + + + + + + + + + + + + + + + ); +} + +export function AcquireXREFIcon() { + return ( + + + + ); +} + +export function AirdropMobileArrowIcon() { + return ( + + + + ); +} + +export function XREFStakingDetails() { + return ( + + + + + + ); +} + +export function MemeFinalistToken({ ...props }) { + return ( + + ); +} diff --git a/src/components/meme/memeAirdropListForPc.tsx b/src/components/meme/memeAirdropListForPc.tsx new file mode 100644 index 000000000..7ee62eeb2 --- /dev/null +++ b/src/components/meme/memeAirdropListForPc.tsx @@ -0,0 +1,178 @@ +import React, { useEffect, useState } from 'react'; +import { ModalClose } from '../../components/icon'; +import { memeComingSoonJson } from '../../config/memeConfig'; +import Modal from 'react-modal'; +import { ftGetTokenMetadata } from '../../services/ft-contract'; +import CustomTooltip from '../customTooltip/customTooltip'; +import { RuleIcon, RuleTips, TriangleDown, TriangleUp } from './icons'; + +export default function MemeAirdropListForPc({ onRequestClose, isOpen }: any) { + // show rules + const [isShowRules, setShowRules] = useState>([]); + const [icons, setIcons] = useState({}); + useEffect(() => { + const waitDealedData = new Array(memeComingSoonJson.length).fill(false); + setShowRules(waitDealedData); + }, []); + // deal rules show/hide + const setShowRulesIndex = (index, flag) => { + //shallow copy + const updatedWaitDealedData = [...isShowRules]; + updatedWaitDealedData[index] = flag; + setShowRules(updatedWaitDealedData); + }; + + useEffect(() => { + setShowRules(new Array(memeComingSoonJson.length).fill(false)); + fetchIcons(); + }, []); + const fetchIcons = async () => { + const newIcons = {}; + const promises = memeComingSoonJson.map(async (item) => { + const iconData = await ftGetTokenMetadata(item.id); + newIcons[item.id] = iconData.icon; + }); + + await Promise.all(promises); + setIcons(newIcons); + }; + const Tip = ` +
+ The airdropped tokens below are not investment advice. +
`; + return ( + +
+ {/* header */} +
+
+ +
+ + +
+
+ +
+ + {/* body */} +
+
+ {/* map */} + {memeComingSoonJson.map((item, index) => { + return ( +
+ {/* left */} + {icons[item.id] ? ( + Icon + ) : null} + + {/* right */} +
+ {/* title */} +
+

{item.title}

+

+ {item.introduce} +

+
+ + {/* amount & droptime */} +
+ {/* left */} +
+
Amount
+

+ + {item.amount} + + {/* + {item.amountDollar} + */} +

+
+ + {/* right */} +
+
+
Airdrop time
+

+ {item.airdropTime} +

+
+
+
+ + {/* rule change */} +
+
+ + Rules +
+ setShowRulesIndex(index, !isShowRules[index]) + } + > + {isShowRules[index] ? ( + + ) : ( + + )} +
+
+ + {/* collase */} + {isShowRules[index] ? ( +
+ {item.rules} +
+ ) : ( + '' + )} +
+
+
+ ); + })} +
+
+
+
+ ); +} diff --git a/src/config/memeConfig.tsx b/src/config/memeConfig.tsx new file mode 100644 index 000000000..25a3cf8a3 --- /dev/null +++ b/src/config/memeConfig.tsx @@ -0,0 +1,60 @@ +export const memeComingSoonJson = [ + { + id: 'dragonsoultoken.near', + title: 'DGS', + introduce: `DGS is the exclusive token of DragonWorld game. + DragonWorld integrates the excellent elements of World of Warcraft and + Palworld, including dragon growth, dragon adventures, single and multiplayer + dungeons, dragon mining, dragon production farms, and a series of other interesting elements.`, + amount: '10000000', + airdropTime: 'June 30', + rules: `1-30: 150000 DGS\n31-100: 50000 DGS\n101-200: 20000 DGS`, + isShow: false, + }, + { + id: 'edge-fast.near', + title: '$FAST', + introduce: `$FAST is the rewards and utility token in the Edge Video AI economy. Interact with TV content and participate in live AI-generated quizzes to be rewarded with virtual points that can be exchanged for $FAST token.`, + amount: '?', + airdropTime: '?', + rules: `Wallet must stake all 4: LONK, NEKO, BLACKDRAGON, SHITZU meme coins on Ref Finance and be in the top 200 stakers across all pools. Equal airdrop for all in top 200, there are no multipliers`, + isShow: false, + }, + // { + // icon: ShillGpt(), + // title: 'shillGpt', + // introduce: `Lorem ipsum dolor sit amet consectetur adipisicing elit. At + // enim exercitationem veniam eos voluptatum pariatur culpa, + // ratione qui quo sunt ipsa labore ullam.`, + // amount: '88,445,820,340.00', + // amountDollar: '$6.27M', + // airdropTime: '4.26-5.26/2024', + // rules: `Lorem, ipsum dolor sit amet consectetur adipisicing elit. + // At ea corporis, vero unde modi perferendis? Velit + // eligendi, nesciunt ipsa sequi voluptatum, non nemo, + // architecto adipisci blanditiis nostrum ex accusantium + // harum?Lorem ipsum dolor sit amet consectetur adipisicing + // elit. Suscipit distinctio iste provident facere a possimus + // numquam officia nostrum ut cumque aperiam repellat quidem + // ex adipisci, quod ad eum dignissimos placeat.`, + // isShow: false, + // }, + // { + // icon: UwonGreen(), + // title: 'Uwon', + // introduce: `Lorem ipsum dolor sit amet consectetur adipisicing elit. At + // ratione qui quo sunt ipsa labore ullam.`, + // amount: '88,445,820,340.00', + // amountDollar: '$6.27M', + // airdropTime: '4.26-5.26/2024', + // rules: `Lorem, ipsum dolor sit amet consectetur adipisicing elit. + // At ea corporis, vero unde modi perferendis? Velit + // eligendi, nesciunt ipsa sequi voluptatum, non nemo, + // architecto adipisci blanditiis nostrum ex accusantium + // harum?Lorem ipsum dolor sit amet consectetur adipisicing + // elit. Suscipit distinctio iste provident facere a possimus + // numquam officia nostrum ut cumque aperiam repellat quidem + // ex adipisci, quod ad eum dignissimos placeat.`, + // isShow: false, + // }, +]; diff --git a/src/pages/MemeAirdropListForMobile.tsx b/src/pages/MemeAirdropListForMobile.tsx new file mode 100644 index 000000000..4cdada9c5 --- /dev/null +++ b/src/pages/MemeAirdropListForMobile.tsx @@ -0,0 +1,157 @@ +import React, { useState, useEffect, useContext } from 'react'; +import Modal from 'react-modal'; +import { ModalClose } from '../components/icon'; +import { memeComingSoonJson } from '../config/memeConfig'; +import { useHistory } from 'react-router-dom'; +import { ftGetTokenMetadata } from '../services/ft-contract'; +import CustomTooltip from '../components/customTooltip/customTooltip'; +import { Goback, RuleIcon, RuleTips } from '../components/meme/icons'; +export default function MemeComingSoon() { + const [icons, setIcons] = useState({}); + // + const [isShowModal, setIsShowModal] = useState(false); + // + const [selectListItem, setSelectListItem] = useState(null); + // + const history = useHistory(); + // + useEffect(() => { + window.scrollTo({ + top: 0, + }); + fetchIcons(); + }, []); + + const fetchIcons = async () => { + const newIcons = {}; + const promises = memeComingSoonJson.map(async (item) => { + const iconData = await ftGetTokenMetadata(item.id); + newIcons[item.id] = iconData.icon; + }); + + await Promise.all(promises); + setIcons(newIcons); + }; + const Tip = ` +
+ The airdropped tokens below are not investment advice. +
`; + return ( +
+ {/* title */} +
+
history.push('/meme')}> + +
+

Airdrop Announcement

+
+ + +
+
+ + {/* air drop list */} + {memeComingSoonJson.map((item, index) => { + return ( +
+ {/* icon and title */} +
+ {icons[item.id] ? ( + Icon + ) : null} + {item.title} +
+ + {/* introduce */} +

+ {item.introduce} +

+ + {/* amount */} +
+
Amount
+

+ {item.amount} +

+
+ + {/* air drop */} +
+
Airdrop time
+

+ {item.airdropTime} +

+
+ + {/* rules */} +
{ + setIsShowModal(!isShowModal); + setSelectListItem(item); + }} + > + + Rules +
+
+ ); + })} + + {/* modal */} + setIsShowModal(false)} + style={{ + overlay: { + backdropFilter: 'blur(4px)', + WebkitBackdropFilter: 'blur(4px)', + }, + content: { + outline: 'none', + }, + }} + > + {/* header */} +
+ {/* */} +
+ +
+ {/* */} + setIsShowModal(false)} + /> +
+ + {/* content */} +
+
+ {selectListItem?.rules} +
+
+
+
+ ); +} diff --git a/src/pages/MemePage.tsx b/src/pages/MemePage.tsx index a9f2ad5fc..32cf497dc 100644 --- a/src/pages/MemePage.tsx +++ b/src/pages/MemePage.tsx @@ -8,6 +8,7 @@ import VoteXREF from '../components/meme/VoteXREF'; import SeedsBox from '../components/meme/SeedsBox'; import WithdrawList from '../components/meme/WithdrawList'; import Countdown from '../components/meme/countdown'; +import Staking from '../components/meme/Staking'; export default function MemePage() { const is_mobile = isMobile(); @@ -24,7 +25,8 @@ export default function MemePage() { {showCountdown && ( )} - + + {/* */}
diff --git a/src/routes.tsx b/src/routes.tsx index 3bb9118b9..a83152f9e 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -244,6 +244,15 @@ const routes: Route[] = [ }), wrapper: 'AutoHeight', }, + { + path: '/airdop', + element: lazy(() => { + return import( + /* webpackChunkName: "memepage" */ 'src/pages/MemeAirdropListForMobile' + ); + }), + wrapper: 'AutoHeight', + }, { path: '/', element: lazy(() => { diff --git a/src/services/api.ts b/src/services/api.ts index 6be2626e8..8094d3bbc 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -54,7 +54,7 @@ export const getPoolBalance = async (pool_id: number) => { return refFiViewFunction({ methodName: 'get_pool_shares', args: { - pool_id: pool_id, + pool_id, account_id: getCurrentWallet()?.wallet?.getAccountId(), }, }).then((balance) => { @@ -158,3 +158,72 @@ export const currentTokensPrice = async (ids: string): Promise => { return []; }); }; + +export const getMemeFarmingTokens = async (): Promise => { + return await fetch(config.memeRankApiUrl + '/meme-farming/tokens', { + method: 'GET', + }) + .then((res) => res.json()) + .then((data) => { + return data; + }) + .catch(() => { + return []; + }); +}; + +export const getMemeFarmingTotalAssetsList = async ( + limit: number, + offset: number, + orderBy: string +): Promise => { + const queryParams = new URLSearchParams({ + limit: limit.toString(), + offset: offset.toString(), + order_by: orderBy, + }); + const url = `${ + config.memeRankApiUrl + }/meme-farming/total?${queryParams.toString()}`; + + return await fetch(url, { + method: 'GET', + }) + .then((res) => res.json()) + .then((data) => { + return data; + }) + .catch(() => { + return []; + }); +}; + +export const getMemeFarmingAssetsList = async ( + token: string, + sort: string, + limit: number, + offset: number, + orderBy: string +): Promise => { + const queryParams = new URLSearchParams({ + token, + sort, + limit: limit.toString(), + offset: offset.toString(), + order_by: orderBy, + }); + const url = `${ + config.memeRankApiUrl + }/meme-farming/list?${queryParams.toString()}`; + + return await fetch(url, { + method: 'GET', + }) + .then((res) => res.json()) + .then((data) => { + return data; + }) + .catch(() => { + return []; + }); +}; diff --git a/src/services/config.ts b/src/services/config.ts index 7e3889e25..f9aab391f 100644 --- a/src/services/config.ts +++ b/src/services/config.ts @@ -151,6 +151,7 @@ export default function getConfig( indexerUrl: 'https://api.ref.finance', sodakiApiUrl: 'https://api.stats.ref.finance/api', txIdApiUrl: 'https://api3.nearblocks.io', + memeRankApiUrl: 'https://api.ref.finance', blackList: process.env.FARM_BLACK_LIST || ['1371#3', '2769#2'], REF_FI_CONTRACT_ID: process.env.REF_FI_CONTRACT_ID || 'v2.ref-finance.near', @@ -320,6 +321,7 @@ export default function getConfig( indexerUrl: 'https://testnet-indexer.ref-finance.com', sodakiApiUrl: 'https://api.stats.ref.finance/api', txIdApiUrl: 'https://api-testnet.nearblocks.io', + memeRankApiUrl: 'https://test.api.cclp.finance', blackList: process.env.FARM_BLACK_LIST || ['1371#3'], REF_FI_CONTRACT_ID: process.env.REF_FI_CONTRACT_ID || 'ref-finance-101.testnet', @@ -419,6 +421,7 @@ export default function getConfig( indexerUrl: 'https://dev-indexer.ref-finance.com', sodakiApiUrl: 'https://api.stats.ref.finance/api', txIdApiUrl: 'https://api-testnet.nearblocks.io', + memeRankApiUrl: 'https://test.api.cclp.finance', blackList: process.env.FARM_BLACK_LIST || ['1371#3'], REF_FI_CONTRACT_ID: process.env.REF_FI_CONTRACT_ID || 'exchange.ref-dev.testnet', @@ -518,6 +521,7 @@ export default function getConfig( indexerUrl: 'https://api.ref.finance', sodakiApiUrl: 'https://api.stats.ref.finance/api', txIdApiUrl: 'https://api3.nearblocks.io', + memeRankApiUrl: 'https://api.ref.finance', blackList: process.env.FARM_BLACK_LIST || ['1371#3', '2769#2'], REF_FI_CONTRACT_ID: process.env.REF_FI_CONTRACT_ID || 'v2.ref-finance.near', diff --git a/src/state/pool.ts b/src/state/pool.ts index 6e27d7c83..eaa085e3b 100644 --- a/src/state/pool.ts +++ b/src/state/pool.ts @@ -1,4 +1,4 @@ -import { useEffect, useState, useCallback, useContext } from 'react'; +import { useEffect, useState, useCallback, useContext, useRef } from 'react'; import { calculateFairShare, percentLess, @@ -110,6 +110,7 @@ import { import { getPointByPrice, getPriceByPoint } from '../services/commonV3'; import { formatPercentage } from '../components/d3Chart/utils'; import { get_account, ILock } from '../services/lp-locker'; +import { introCurrentPageStore } from '../stores/introCurrentPage'; const REF_FI_STABLE_POOL_INFO_KEY = `REF_FI_STABLE_Pool_INFO_VALUE_${ getConfig().STABLE_POOL_ID @@ -143,7 +144,7 @@ export const useBatchTotalShares = ( const [accountLocked, setAccountLocked] = useState>(); const getFarmStake = (pool_id: number) => { - let farmStake = '0'; + const farmStake = '0'; const seedIdList: string[] = Object.keys(finalStakeList); let tempFarmStake: string | number = '0'; @@ -288,6 +289,32 @@ interface LoadPoolsOpts { order?: string; } +export function useScrollToTopOnFirstPage() { + const hasGuided = JSON.parse(localStorage.getItem('hasGuided')); + + const introRef = useRef(null); + const { currentPage, hasLoaingOver } = introCurrentPageStore() as any; + + useEffect(() => { + if (introRef.current && hasLoaingOver) { + const rect = introRef.current.getBoundingClientRect(); + const offset = window.innerHeight / 2; + const scrollTop = rect.top + window.pageYOffset - offset; + + window.scroll({ + top: scrollTop, + behavior: 'smooth', + }); + + // introRef.current.scrollIntoView({ + // behavior: 'smooth', + // }); + } + }, [currentPage, hasLoaingOver]); // + // + return { introRef, currentPage, hasGuided }; +} + export const usePools = (props: { searchTrigger?: Boolean; tokenName?: string; diff --git a/src/stores/introCurrentPage.ts b/src/stores/introCurrentPage.ts new file mode 100644 index 000000000..10964712f --- /dev/null +++ b/src/stores/introCurrentPage.ts @@ -0,0 +1,10 @@ +import { create } from 'zustand'; + +export const introCurrentPageStore = create((set, get: any) => ({ + currentPage: 1, + hasLoaingOver: false, + setCurrentPage: (currentPage) => set({ currentPage }), + getCurrentPage: () => get().currentPage, + setHasLoaingOver: (hasLoaingOver) => set({ hasLoaingOver }), + getHasLoaingOver: () => get().hasLoaingOver, +})); diff --git a/src/utils/menu.tsx b/src/utils/menu.tsx index 7ae8d66c1..63fdd62aa 100644 --- a/src/utils/menu.tsx +++ b/src/utils/menu.tsx @@ -501,7 +501,7 @@ export const useMenus = (cb?: () => void) => { label: <>Meme Season, url: '/meme', isExternal: false, - links: ['/meme'], + links: ['/meme', '/airdop'], }, { id: '6', @@ -875,7 +875,7 @@ export const useMenusMobile = (setShow: (show: boolean) => void) => { label: <>Meme Season, url: '/meme', isExternal: false, - links: ['/meme'], + links: ['/meme', '/airdop'], }, { id: '6', diff --git a/tailwind.config.js b/tailwind.config.js index e603c19f2..7fc48b20e 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -136,6 +136,7 @@ module.exports = { pendingBgColor: 'linear-gradient(180deg, #2F3A39 0%, #1D2932 100%)', memeVoteBgColor: 'linear-gradient(180deg, #213441 0%, #15242F 100%)', bannerBtnBgColor: 'linear-gradient(180deg, #00FFD1 0%, #00997D 100%)', + memeStakingBgColor: 'linear-gradient(180deg, #213441 0%, #15242F 100%)', }), gridTemplateColumns: { farmSearch: '2fr 1fr', @@ -442,6 +443,10 @@ module.exports = { poolDetaileTxBgColor: '#1D2833', poolDetaileTxBorderColor: '#2D343D', poolDetaileTxHoverColor: '#2C3842', + memeStakingBorderColor: '#20323E', + memeUserStackeBgColor: '#213441', + memeUserStackeMobileBgColor: '#44525D', + memePaginationComponentColor: '#6A7279', }, fontFamily: { sans: ['Poppins', ...defaultTheme.fontFamily.sans], @@ -520,7 +525,8 @@ module.exports = { fontSize: { '13px': '13px', '10px': '10px', - '14px':'14px', + '14px': '14px', + '26px': '26px', }, maxHeight: {