diff --git a/.env.development b/.env.development index 8f52936..349ed31 100644 --- a/.env.development +++ b/.env.development @@ -24,8 +24,8 @@ REACT_APP_SNAPSHOT_VOTING_API = "https://xtjyd0liqe.execute-api.ap-northeast-1.a # REACT_APP_REQUEST_URL = 'http://192.253.237.94:9090' -REACT_APP_REQUEST_URL = 'http://HousedeMacBook-Air.local:8080' -# REACT_APP_REQUEST_URL = 'http://101.35.117.69:9090' +# REACT_APP_REQUEST_URL = 'http://HousedeMacBook-Air.local:8080' +REACT_APP_REQUEST_URL = 'http://101.35.117.69:9090' # REACT_APP_REQUEST_URL = 'http://192.168.2.147:8080' # REACT_APP_REQUEST_URL = 'http://192.168.2.:8080' # REACT_APP_REQUEST_URL = 'http://6o7g1fv83e.51xd.pub' diff --git a/build.zip b/build.zip new file mode 100644 index 0000000..dca428d Binary files /dev/null and b/build.zip differ diff --git a/public/images/empty.svg b/public/images/empty.svg new file mode 100644 index 0000000..ee787c8 --- /dev/null +++ b/public/images/empty.svg @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/public/locales/zh-CN.json b/public/locales/zh-CN.json index ba3d0b9..94a1037 100644 --- a/public/locales/zh-CN.json +++ b/public/locales/zh-CN.json @@ -1184,6 +1184,7 @@ "social contact demo": "社交demo", "The rate of": "出率", "%hour%hour": "%hour%小时", + "%minute%minute": "%minute%分钟", "limit the quantity of": "限量", "time limit": "限时", "nft Smoking in the probability": "NFT抽中概率", @@ -1213,12 +1214,12 @@ "NFT name (grade) of successful synthesis = NFT name (grade) of raw material x quantity + synthesis cost": "合成成功NFT名称(等级)=原料NFT名称(等级)×数量+合成费用", "bazaar": "市场", "auction": "拍卖", - "I sell": "我的售卖", + "I want to sell": "我要售卖", "transaction record": "交易记录", "The total volume": "总交易额", "The total number of transactions": "总交易数", "Total number of auctions": "总拍卖次数", - "Total auction commission": "总拍卖返佣", + "Total transaction rebate": "总交易返佣", "trading value": "出售价格", "Detail": "详情", @@ -1269,5 +1270,19 @@ "unit price": "单价", "seller": "卖方", "purchaser": "买方", - "trading hour": "交易时间" + "trading hour": "交易时间", + "Please select quantity": "请选择数量", + "No data yet": "还没有数据哦~", + "purchase succeeds": "购买成功", + "Copy success": "复制成功", + "NFT Holder": "NFT 持有者", + "NFT With the total": "NFT拥有总量", + "official market": "官方市场", + "finished": "已结束", + "have not started": "未开始", + "category": "类别", + "GIVING": "礼物", + "PROPS": "道具", + "rarity": "稀有度", + "success": "成功" } diff --git a/src/components/Empty/index.tsx b/src/components/Empty/index.tsx new file mode 100644 index 0000000..00da137 --- /dev/null +++ b/src/components/Empty/index.tsx @@ -0,0 +1,31 @@ +import React from 'react' +import styled, { keyframes } from 'styled-components' +import { useTranslation } from 'contexts/Localization' +import { Flex, Text, Image } from '@pancakeswap/uikit' + +interface EmptyProps { + title?: string + marginTop?: string +} + +const EmptyFlex = styled(Flex)` + justify-content: center; + align-items: center; + flex-direction: column; +` +const EmptyText = styled(Text)` + font-size: 16px; + color: #505f79; + margin-top: 12px; +` + +const Empty: React.FC = ({ title, marginTop = '20px' }) => { + const { t } = useTranslation() + return ( + + + {title ? t(title) : t('No data yet')} + + ) +} +export default Empty diff --git a/src/config/abi/hccNFTHolderPool.json b/src/config/abi/holderPool.json similarity index 100% rename from src/config/abi/hccNFTHolderPool.json rename to src/config/abi/holderPool.json diff --git a/src/config/constants/contracts.ts b/src/config/constants/contracts.ts index 6c90efb..1d40adc 100644 --- a/src/config/constants/contracts.ts +++ b/src/config/constants/contracts.ts @@ -29,6 +29,11 @@ export default { 56: '0x9ee1c805a9508c0799b157ebbe3108d57c8d8588', // NEED CHANGE 持币人分红 5: '0xd91740ABe3C54f9e9D8A2c80615618a5dd234556', }, + holderPool: { + 97: '0xb9d1567848771b984f0330c49933b27f1dea0675', + 56: '0xb9d1567848771b984f0330c49933b27f1dea0675', // NEED CHANGE NFT持有者 + 5: '0xb9d1567848771b984f0330c49933b27f1dea0675', + }, referralChef: { 97: '0x0866962d208e91ea8804db3f547cccf22fe39ea7', 56: '0x0866962d208e91ea8804db3f547cccf22fe39ea7', // NEED CHANGE 邀请或则军团长 @@ -47,7 +52,7 @@ export default { blindBox: { 97: '0x0e226d7b83b511ce224803b1330beb4a59bfa5d6', 56: '0x0e226d7b83b511ce224803b1330beb4a59bfa5d6', // NEED CHANGE 官方市场 盲盒 - 5: '0x0e226d7b83b511ce224803b1330beb4a59bfa5d6', + 5: '0x99d61f4724d520246c43d0f1dea22c1f21c42a4e', }, lotteryV2: { 97: '0x5790c3534F30437641541a0FA04C992799602998', @@ -57,7 +62,7 @@ export default { multiCall: { 56: '0xfF6FD90A470Aaa0c1B8A54681746b07AcdFedc9B', 97: '0x8F3273Fb89B075b1645095ABaC6ed17B2d4Bc576', - 5: '0xfF6FD90A470Aaa0c1B8A54681746b07AcdFedc9B', + 5: '0x3a648f94783e35526072c02d7ce89c385b41ac19', }, pancakeProfile: { 56: '0xDf4dBf6536201370F95e06A0F8a7a70fE40E388a', diff --git a/src/config/localization/translations.json b/src/config/localization/translations.json index d9c21b3..95161c3 100644 --- a/src/config/localization/translations.json +++ b/src/config/localization/translations.json @@ -1311,6 +1311,7 @@ "social contact demo": "social contact demo", "The rate of": "The rate of", "%hour%hour": "%hour%hour", + "%minute%minute": "%minute%minute", "limit the quantity of": "limit the quantity of", "time limit": "time limit", "nft Smoking in the probability": "nft Smoking in the probability", @@ -1340,12 +1341,12 @@ "NFT name (grade) of successful synthesis = NFT name (grade) of raw material x quantity + synthesis cost": "NFT name (grade) of successful synthesis = NFT name (grade) of raw material x quantity + synthesis cost", "bazaar": "bazaar", "auction": "auction", - "I sell": "I sell", + "I want to sell": "I want to sell", "transaction record": "transaction record", "The total volume": "The total volume", "The total number of transactions": "The total number of transactions", "Total number of auctions": "Total number of auctions", - "Total auction commission": "Total auction commission", + "Total transaction rebate": "Total transaction rebate", "trading value": "trading value", "Detail": "Detail", @@ -1396,5 +1397,19 @@ "unit price": "unit price", "seller": "seller", "purchaser": "purchaser", - "trading hour": "trading hour" + "trading hour": "trading hour", + "Please select quantity": "Please select quantity", + "No data yet": "No data yet~", + "purchase succeeds": "purchase succeeds", + "Copy success": "Copy success", + "NFT Holder": "NFT Holder", + "NFT With the total": "NFT With the total", + "official market": "official market", + "finished": "finished", + "have not started": "have not started", + "category": "category", + "GIVING": "GIVING", + "PROPS": "PROPS", + "rarity": "rarity", + "success": "success" } diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts index a6fb3cf..17c2149 100644 --- a/src/hooks/useContract.ts +++ b/src/hooks/useContract.ts @@ -28,7 +28,8 @@ import { getBoardchefContract, getReferralRewardchefContract, getIdoPurchaseContract, - getHccGiftNftContract, + getBlindBoxContract, + getHolderPoolContract, } from 'utils/contractHelpers' // Imports below migrated from Exchange useContract.ts @@ -115,9 +116,13 @@ export const useIdoPurchase = () => { const { library } = useActiveWeb3React() return useMemo(() => getIdoPurchaseContract(library.getSigner()), [library]) } -export const useHccGiftNft = () => { +export const useBlindBox = () => { const { library } = useActiveWeb3React() - return useMemo(() => getHccGiftNftContract(library.getSigner()), [library]) + return useMemo(() => getBlindBoxContract(library.getSigner()), [library]) +} +export const useHolderPool = () => { + const { library } = useActiveWeb3React() + return useMemo(() => getHolderPoolContract(library.getSigner()), [library]) } export const useReferralRewardchef = () => { const { library } = useActiveWeb3React() diff --git a/src/services/bazaar.ts b/src/services/bazaar.ts index 65268fa..d4308d3 100644 --- a/src/services/bazaar.ts +++ b/src/services/bazaar.ts @@ -7,5 +7,33 @@ export const getOfficialPage = (params) => { params, }) } +export const getOfficialPurchase = (id, params) => { + return request.request({ + url: `/high_city/app/api/market/official/purchase/${id}`, + method: 'get', + params, + }) +} +export const checkBuyResult = (params) => { + return request.request({ + url: `/high_city/app/api/market/official/purchase/tx`, + method: 'get', + params, + }) +} +export const getPurchaseRecord = (params) => { + return request.request({ + url: `/high_city/app/api/market/official/purchase/record`, + method: 'get', + params, + }) +} +export const getNftDetail = (id, params) => { + return request.request({ + url: `/high_city/app/api/nft/detail/${id}`, + method: 'get', + params, + }) +} export default getOfficialPage diff --git a/src/services/blindBox.ts b/src/services/blindBox.ts index 5d7d633..e84cef8 100644 --- a/src/services/blindBox.ts +++ b/src/services/blindBox.ts @@ -14,10 +14,18 @@ export const getBoxDetail = (id) => { method: 'get', }) } -export const getPurchase = (id) => { +export const getPurchase = (id, params) => { return request.request({ url: `/high_city/app/api/box/purchase/${id}`, method: 'get', + params, + }) +} +export const checkBuyResult = (params) => { + return request.request({ + url: `/high_city/app/api/box/purchase/tx`, + method: 'get', + params, }) } diff --git a/src/services/nftBox.ts b/src/services/nftBox.ts new file mode 100644 index 0000000..dac908b --- /dev/null +++ b/src/services/nftBox.ts @@ -0,0 +1,18 @@ +import request from 'utils/request' + +export const getSelfPage = (params) => { + return request.request({ + url: '/high_city/app/api/nft/self/page', + method: 'get', + params, + }) +} +export const getNftDetails = (token, params) => { + return request.request({ + url: `/high_city/app/api/nft/detail/${token}`, + method: 'get', + params, + }) +} + +export default getSelfPage diff --git a/src/types/bazaar.ts b/src/types/bazaar.ts index aa619da..413f290 100644 --- a/src/types/bazaar.ts +++ b/src/types/bazaar.ts @@ -6,6 +6,7 @@ export interface ListProps { price?: undefined priceList?: PriceProps[] type?: string + record?: boolean } export interface CoverResourceProps { path?: string diff --git a/src/utils/addressHelpers.ts b/src/utils/addressHelpers.ts index 2267be3..3074de6 100644 --- a/src/utils/addressHelpers.ts +++ b/src/utils/addressHelpers.ts @@ -80,3 +80,6 @@ export const getIdoPurchaseAddress = () => { export const getBlindBoxAddress = () => { return getAddress(addresses.blindBox) } +export const getHolderPoolAddress = () => { + return getAddress(addresses.holderPool) +} diff --git a/src/utils/calls/boards.ts b/src/utils/calls/boards.ts index eeeda45..e57ee92 100644 --- a/src/utils/calls/boards.ts +++ b/src/utils/calls/boards.ts @@ -13,6 +13,7 @@ export const stakeBoard = async (masterChefContract, pid, amount) => { } export const unstakeBoard = async (masterChefContract) => { + console.log(masterChefContract) const tx = await masterChefContract.withdrawHCC(options) const receipt = await tx.wait() return receipt.status diff --git a/src/utils/contractHelpers.ts b/src/utils/contractHelpers.ts index a026fcd..ee6dd15 100644 --- a/src/utils/contractHelpers.ts +++ b/src/utils/contractHelpers.ts @@ -29,6 +29,7 @@ import { getReferralRewardAddress, getIdoPurchaseAddress, getBlindBoxAddress, + getHolderPoolAddress, } from 'utils/addressHelpers' // ABI @@ -62,6 +63,7 @@ import MultiCallAbi from 'config/abi/Multicall.json' import bunnySpecialCakeVaultAbi from 'config/abi/bunnySpecialCakeVault.json' import bunnySpecialPredictionAbi from 'config/abi/bunnySpecialPrediction.json' import idoPurchase from 'config/abi/idoPurchase.json' +import holderPool from 'config/abi/holderPool.json' import blindBox from 'config/abi/blindBox.json' import { ChainLinkOracleContract, PredictionsContract } from './types' @@ -134,7 +136,10 @@ export const getReferralRewardchefContract = (signer?: ethers.Signer | ethers.pr export const getIdoPurchaseContract = (signer?: ethers.Signer | ethers.providers.Provider) => { return getContract(idoPurchase, getIdoPurchaseAddress(), signer) } -export const getHccGiftNftContract = (signer?: ethers.Signer | ethers.providers.Provider) => { +export const getHolderPoolContract = (signer?: ethers.Signer | ethers.providers.Provider) => { + return getContract(holderPool, getHolderPoolAddress(), signer) +} +export const getBlindBoxContract = (signer?: ethers.Signer | ethers.providers.Provider) => { return getContract(blindBox, getBlindBoxAddress(), signer) } export const getClaimRefundContract = (signer?: ethers.Signer | ethers.providers.Provider) => { diff --git a/src/views/Bazaar/components/AssetsInfo.tsx b/src/views/Bazaar/components/AssetsInfo.tsx new file mode 100644 index 0000000..6c55fe1 --- /dev/null +++ b/src/views/Bazaar/components/AssetsInfo.tsx @@ -0,0 +1,49 @@ +import React, { useState, useEffect } from 'react' +import styled from 'styled-components' +import { useTranslation } from 'contexts/Localization' +import { Flex, Button, Text, Image } from '@pancakeswap/uikit' +import { getContract } from 'services/referral' +import FlexCom from './FlexCom' + +interface DetailProps { + typeIndex: number +} + +const DetailFlex = styled(Flex)` + flex-direction: column; + width: 100%; + height: 278px; + background: #f5ffff; + opacity: 1; + border-radius: 20px; + padding: 24px 30px; + margin-top: 24px; +` + +const AssetsInfo: React.FC = ({ typeIndex }) => { + const { t } = useTranslation() + const [link, setLink] = useState('') + + const getLinkAddress = async () => { + const data = await getContract() + setLink(data) + } + useEffect(() => { + getLinkAddress() + }, []) + + return ( + + {typeIndex !== 0 && } + + {typeIndex !== 0 && } + + + + ) +} +export default AssetsInfo diff --git a/src/views/Bazaar/components/BtnStatus.tsx b/src/views/Bazaar/components/BtnStatus.tsx new file mode 100644 index 0000000..40978da --- /dev/null +++ b/src/views/Bazaar/components/BtnStatus.tsx @@ -0,0 +1,182 @@ +import React, { useState, useEffect } from 'react' +import styled from 'styled-components' +import { useTranslation } from 'contexts/Localization' +import { Flex, Button, Text, Image } from '@pancakeswap/uikit' +import { useAccount } from 'state/userInfo/hooks' +import { fetchBlindBoxUserAllowances } from 'state/blindBox' +import { getAddress } from 'utils/addressHelpers' +import { useERC20 } from 'hooks/useContract' +import tokens from 'config/constants/tokens' +import useRefresh from 'hooks/useRefresh' +import useToast from 'hooks/useToast' +import { ListProps } from 'types/bazaar' +import { checkBuyResult } from 'services/bazaar' +import UnlockButton from 'components/UnlockButton' +import { useBuyTransaction, useApproveHcc } from '../hooks' +import { useCheckHccTokenBalance, useCheckTokenBalance } from '../../BlindBox/hooks' + +interface DetailProps { + detail: ListProps +} + +const PriceButton = styled(Button)` + width: 100%; + height: 60px; + background: linear-gradient(269deg, #1fc8d3 0%, #1fd4b0 100%); + border-radius: 30px; + font-size: 16px; + margin: 30px 0; +` +const AuthorizationBtn = styled(Button)` + width: 100%; + height: 60px; + background: linear-gradient(269deg, #1fc8d3 0%, #1fd4b0 100%); + border-radius: 30px; + font-size: 16px; + margin: 30px 0; +` + +const UnlockButtonDiv = styled(UnlockButton)` + width: 100%; + height: 60px; + background: linear-gradient(269deg, #1fc8d3 0%, #1fd4b0 100%); + border-radius: 30px; + font-size: 16px; + margin: 30px 0; +` + +const BtnStatus: React.FC = ({ detail }) => { + const { t } = useTranslation() + const [txId, setTxId] = useState() + const [price, setPrice] = useState('') + const [allowanceList, setAllowanceList] = useState({ USDT: 0, HCC: 0 }) + const [loading, setLoading] = useState(false) + const { toastSuccess, toastError } = useToast() + const { fastRefresh } = useRefresh() + const account = useAccount() + // const [link, setLink] = useState('') + const [buyVisible, setBuyVisible] = useState(false) + + const usdtContract = useERC20(getAddress(tokens.usdt.address)) + const hccContract = useERC20(getAddress(tokens.hcc.address)) + const { onApprove: onUsdtApprove } = useApproveHcc(usdtContract) + const { onApprove: onHccApprove } = useApproveHcc(hccContract) + + const getAllowances = async () => { + const allowances = await fetchBlindBoxUserAllowances(account) + detail.priceList && + detail.priceList.forEach((item) => { + if (detail.priceList.length === 2) { + if (allowances.usdt && allowances.hcc) { + setBuyVisible(true) + } + } else { + console.log(' ') + if (allowances.hcc && item.label === 'HCC') { + setBuyVisible(true) + } else if (allowances.usdt && item.label === 'USDT') { + setBuyVisible(true) + } + } + }) + setAllowanceList({ + USDT: allowances.usdt, + HCC: allowances.hcc, + }) + } + + useEffect(() => { + if (detail.priceList) { + const priceList = detail.priceList.map((item) => { + return `${Number(item.value).toFixed(3)} ${item.label}` + }) + setPrice(priceList.join('-')) + } + if (account) { + getAllowances() + } + }, [account]) + const buyTransaction = useBuyTransaction() + + const [onCheckBalanceHcc] = useCheckHccTokenBalance() + const [onCheckBalanceUsdt] = useCheckTokenBalance() + const handleBuy = async () => { + let HccBalance = true + detail.priceList && + detail.priceList.forEach((item) => { + if (item.label === 'HCC') { + HccBalance = onCheckBalanceHcc(Number(item.value)) + } else { + HccBalance = onCheckBalanceUsdt(Number(item.value)) + } + }) + if (!HccBalance) { + return + } + setLoading(true) + const res = await buyTransaction(detail.id, { + id: detail.id, + num: 1, + }) + setTxId(res.hash) + } + + const getTransactionResult = async () => { + const res = await checkBuyResult({ tx: txId }) + if (res) { + setLoading(false) + setTxId(undefined) + toastSuccess(t('purchase succeeds')) + } + } + useEffect(() => { + if (txId && loading) { + getTransactionResult() + } + }, [fastRefresh]) + + const handleApprove = async (approve) => { + try { + setLoading(true) + await approve() + setLoading(false) + getAllowances() + } catch (e) { + console.error(e) + } + } + + return ( + <> + {buyVisible && {t('Buy It Now')}} + + {detail.priceList.map((item, i) => + item.label === 'HCC' && !allowanceList.HCC ? ( + { + handleApprove(onHccApprove) + }} + > + {t('Approve %coin% Contract', { coin: 'HCC' })} + + ) : item.label === 'USDT' && !allowanceList.USDT ? ( + { + handleApprove(onUsdtApprove) + }} + > + {t('Approve %coin% Contract', { coin: 'USDT' })} + + ) : ( + '' + ), + )} + + + ) +} +export default BtnStatus diff --git a/src/views/Bazaar/components/Content.tsx b/src/views/Bazaar/components/Content.tsx index f8c400e..ec94dbd 100644 --- a/src/views/Bazaar/components/Content.tsx +++ b/src/views/Bazaar/components/Content.tsx @@ -1,12 +1,16 @@ import React, { useState, useEffect } from 'react' +import qs from 'querystring' +import { useLocation, useHistory, useParams } from 'react-router-dom' import styled, { keyframes } from 'styled-components' import { useTranslation } from 'contexts/Localization' import Pagination from '@mui/material/Pagination' import { Flex, Text, Input, Image, Dropdown } from '@pancakeswap/uikit' import { useAccount } from 'state/userInfo/hooks' import useRefresh from 'hooks/useRefresh' +import useToast from 'hooks/useToast' import { ListProps } from 'types/bazaar' +import Empty from 'components/Empty' import { useGetOfficialPage } from '../hooks' import HeaderOperation from './HeaderOperation' import ContentShop from './ContentShop' @@ -129,10 +133,13 @@ const FlexOption = styled(Flex)` const Content: React.FC = () => { const { t } = useTranslation() + const location = useLocation() + const history = useHistory() + const { toastSuccess, toastError } = useToast() const [detail, setDetail] = useState({}) const [detailVisible, setDetailVisible] = useState(false) const typeList = [ - { label: t('All'), type: 1 }, + { label: t('official market'), type: 1 }, { label: t('bazaar'), type: 2 }, { label: t('auction'), type: 3 }, ] @@ -155,10 +162,10 @@ const Content: React.FC = () => { const [list, setList] = useState([]) const statusList = [ { label: t('All'), id: '1', grade: '' }, + { label: t('common'), id: '5', grade: 'NORMAL' }, + { label: t('uncommon'), id: '4', grade: 'RARE' }, { label: t('epic'), id: '2', grade: 'EPIC' }, { label: t('legend'), id: '3', grade: 'LEGEND' }, - { label: t('uncommon'), id: '4', grade: 'RARE' }, - { label: t('common'), id: '5', grade: 'NORMAL' }, ] const [statusIndex, setStatusIndex] = useState(0) @@ -173,6 +180,7 @@ const Content: React.FC = () => { const params = { page: pageNum, size: 10, + name: searchTitle, grade: searchGrade, } const data = await getOfficialPage(params) @@ -187,6 +195,15 @@ const Content: React.FC = () => { arr.push(obj) }) setList(arr) + if (location.search) { + const locationData = qs.parse(location.search.slice(1)) + arr.forEach((item) => { + if (item.id === locationData.id) { + setDetail(item) + setDetailVisible(true) + } + }) + } setCount(getTotalPageNum(data.total, data.size)) } @@ -194,16 +211,19 @@ const Content: React.FC = () => { getData() }, [pageNum, searchGrade]) + useEffect(() => { + setPriceSelect({ label: t('Prices go from low to high'), value: '3' }) + }, [t]) + const handleChange = (evt: React.ChangeEvent) => { const { value } = evt.target setSearchTitle(value) } const searchList = () => { - console.log('search') setPage(1) + getData() } const showDetail = (val) => { - console.log(val) setDetail(val) setDetailVisible(!detailVisible) } @@ -220,10 +240,27 @@ const Content: React.FC = () => { setGrade(grade) setPage(1) } + const changePage = (index) => { + if (index !== 0) { + toastError(t('This page is not currently open')) + return + } + setTypeIndex(index) + } + const lookDetail = (val) => { + setDetail(val) + setDetailVisible(true) + // detail + } + + const closeDetail = () => { + history.push('/bazaar') + setDetailVisible(false) + } return ( <> - {detailVisible && setDetailVisible(false)} />} + {detailVisible && } {!detailVisible && ( <> @@ -233,7 +270,7 @@ const Content: React.FC = () => { return ( setTypeIndex(index)} + onClick={() => changePage(index)} className={typeIndex === index ? 'active' : ''} > {item.label} @@ -242,7 +279,7 @@ const Content: React.FC = () => { })} - + lookDetail(v)} /> @@ -281,25 +318,27 @@ const Content: React.FC = () => { )} - - - - {priceSelect.label} - - } - > - {newPrice.map((item) => { - return ( - setPriceSelect(item)}> - {item.label} - - ) - })} - - + {typeIndex !== 0 && ( + + + + {priceSelect.label} + + } + > + {newPrice.map((item) => { + return ( + setPriceSelect(item)}> + {item.label} + + ) + })} + + + )} @@ -311,9 +350,12 @@ const Content: React.FC = () => { showDetail(v)} /> - - - + {list.length > 0 && ( + + + + )} + {list.length === 0 && } )} diff --git a/src/views/Bazaar/components/ContentShop.tsx b/src/views/Bazaar/components/ContentShop.tsx index 90b036f..e723e12 100644 --- a/src/views/Bazaar/components/ContentShop.tsx +++ b/src/views/Bazaar/components/ContentShop.tsx @@ -3,6 +3,7 @@ import styled, { keyframes } from 'styled-components' import { useTranslation } from 'contexts/Localization' import { formatTimeNumber } from 'utils/formatBalance' import { Flex, Text, Image } from '@pancakeswap/uikit' +import useToast from 'hooks/useToast' import { ListProps } from 'types/bazaar' import { useAccount } from 'state/userInfo/hooks' import useRefresh from 'hooks/useRefresh' @@ -67,6 +68,8 @@ const FooterValue = styled(Flex)` const ContentShop: React.FC = ({ list, getDetail }) => { const { t } = useTranslation() + const account = useAccount() + const { toastSuccess, toastError } = useToast() const showDetail = (id) => { getDetail(id) @@ -80,7 +83,7 @@ const ContentShop: React.FC = ({ list, getDetail }) => { .linkText:hover { + color: #1fc7d4 !important; + border-bottom: 1px solid #1fc7d4 !important; + } ` const TextLink = styled(Text)` cursor: pointer; @@ -46,6 +50,7 @@ const FlexCom: React.FC = ({ color={rightColor} style={{ color: textColor, borderBottom: `1px solid ${textColor}` }} onClick={openPage} + className="linkText" > {value} diff --git a/src/views/Bazaar/components/HeaderOperation.tsx b/src/views/Bazaar/components/HeaderOperation.tsx index 6724deb..ce67c1a 100644 --- a/src/views/Bazaar/components/HeaderOperation.tsx +++ b/src/views/Bazaar/components/HeaderOperation.tsx @@ -11,6 +11,7 @@ import SellModal from './SellModal' interface HeaderOperationProps { activeIndex?: number + getDetail?: (v) => void } const HeaderButton = styled(Button)` @@ -34,16 +35,18 @@ const HeaderButton = styled(Button)` } ` -const HeaderOperation: React.FC = ({ activeIndex }) => { +const HeaderOperation: React.FC = ({ activeIndex, getDetail }) => { const { t } = useTranslation() - const [onTransactionRecord] = useModal() + const [onTransactionRecord] = useModal( + getDetail(v)} />, + ) const [onAuctionRecord] = useModal() const [onSellModal] = useModal() return ( - {t('I sell')} + {t('I want to sell')} {/* 当顶部切换选中的是全部和市场则显示交易记录,选中拍卖时展示拍卖纪录 */} {activeIndex === 2 ? ( {t('Auctions a record')} diff --git a/src/views/Bazaar/components/ShopDetail.tsx b/src/views/Bazaar/components/ShopDetail.tsx index 88c6107..2ecec3d 100644 --- a/src/views/Bazaar/components/ShopDetail.tsx +++ b/src/views/Bazaar/components/ShopDetail.tsx @@ -1,21 +1,25 @@ import React, { useState, useEffect } from 'react' -import styled, { keyframes } from 'styled-components' +import styled from 'styled-components' import { useTranslation } from 'contexts/Localization' -import { Heading, Flex, Button, Text, Input, Image, useModal } from '@pancakeswap/uikit' +import { Flex, Button, Text, Image } from '@pancakeswap/uikit' import { useAccount } from 'state/userInfo/hooks' import useRefresh from 'hooks/useRefresh' +import useToast from 'hooks/useToast' import { ListProps } from 'types/bazaar' -import { getContract } from 'services/referral' +import { checkBuyResult } from 'services/bazaar' +import UnlockButton from 'components/UnlockButton' import ShopList from './ShopList' import FlexCom from './FlexCom' import AuctionTable from './AuctionTable' import TransactionTable from './TransactionTable' import AuctionRule from './AuctionRule' +import AssetsInfo from './AssetsInfo' +import BtnStatus from './BtnStatus' interface DetailProps { close: () => void detail: ListProps - typeIndex: number | string + typeIndex: number } const HeaderFlex = styled(Flex)` @@ -35,7 +39,7 @@ const ShopText = styled(Text)` const MainFlex = styled(Flex)` margin-top: 36px; padding: 30px; - background: rgba(255, 255, 255, 0.39); + background: #fff; box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.16); border-radius: 20px; align-items: center; @@ -51,23 +55,17 @@ const MainFlex = styled(Flex)` ` const ShopFlex = styled(Flex)` width: 476px; - height: 590px; flex-direction: column; - background: #fff; - box-shadow: 0px 1px 8px rgba(0, 0, 0, 0.15); - opacity: 1; border-radius: 20px; position: relative; ` -const Detail = styled.div` +const Detail = styled(Flex)` width: 614px; - height: 590px; - /* min-height: 580px; */ + flex-direction: column; + justify-content: space-between; + /* height: 590px; */ box-sizing: border-box; - background: rgba(245, 255, 255, 0.39); - box-shadow: 0px 2px 8px rgba(0, 67, 70, 0.15); border-radius: 20px; - padding: 20px 30px; margin-left: 30px; ${({ theme }) => theme.mediaQueries.xs} { margin-top: 30px; @@ -84,7 +82,6 @@ const TitleText = styled(Text)` font-size: 28px; color: #333333; text-align: center; - margin-top: 8px; ` const PriceButton = styled(Button)` width: 100%; @@ -94,18 +91,78 @@ const PriceButton = styled(Button)` font-size: 16px; margin: 30px 0; ` +const AuthorizationBtn = styled(Button)` + width: 100%; + height: 60px; + background: linear-gradient(269deg, #1fc8d3 0%, #1fd4b0 100%); + border-radius: 30px; + font-size: 16px; + margin: 30px 0; +` + +const UnlockButtonDiv = styled(UnlockButton)` + width: 100%; + height: 60px; + background: linear-gradient(269deg, #1fc8d3 0%, #1fd4b0 100%); + border-radius: 30px; + font-size: 16px; + margin: 30px 0; +` +const DetailFlexInfo = styled(Flex)` + flex-direction: column; + width: 100%; + height: 304px; + background: #f5ffff; + box-shadow: 0px 2px 8px rgba(0, 67, 70, 0.15); + border-radius: 20px; + padding: 26px 30px; +` +const DetailHeaderFlex = styled(Flex)` + justify-content: space-between; + align-items: center; +` const ShopDetail: React.FC = ({ close, detail, typeIndex }) => { const { t } = useTranslation() - const [link, setLink] = useState('') + const [txId, setTxId] = useState() + const [price, setPrice] = useState('') + const [loading, setLoading] = useState(false) + const { toastSuccess, toastError } = useToast() + const { fastRefresh } = useRefresh() + const account = useAccount() + useEffect(() => { + if (detail.priceList) { + const priceList = detail.priceList.map((item) => { + return `${Number(item.value).toFixed(3)} ${item.label}` + }) + setPrice(priceList.join('-')) + } + }, [account]) - const getLinkAddress = async () => { - const data = await getContract() - setLink(data) + const getTransactionResult = async () => { + const res = await checkBuyResult({ tx: txId }) + if (res) { + setLoading(false) + setTxId(undefined) + toastSuccess(t('purchase succeeds')) + } } useEffect(() => { - getLinkAddress() - }, []) + if (txId && loading) { + getTransactionResult() + } + }, [fastRefresh]) + + const getLink = () => { + const createInput = document.createElement('input') + createInput.value = `${window.location.href}?id=${detail.id}` + document.body.appendChild(createInput) + createInput.select() + document.execCommand('Copy') + createInput.remove() + toastSuccess(t('Copy success')) + } + return ( <> @@ -118,52 +175,57 @@ const ShopDetail: React.FC = ({ close, detail, typeIndex }) => { item={detail} width={476} height={606} - img={detail.coverResource.url} + img={detail?.coverResource.url} grade={detail.grade} borderRadius="20px" /> - - - - {detail.name} - {typeIndex !== 0 && ( + + + {detail.name} + + + {typeIndex !== 0 && ( + + )} - )} - - {typeIndex === 0 ? ( - {t('Buy It Now')} - ) : ( - {t('Fixed markup (%price% premium)', { price: '10%' })} - )} - {typeIndex !== 0 && } - {/* */} - - {typeIndex !== 0 && } - - + {detail.record ? ( + '' + ) : typeIndex === 0 ? ( + !account ? ( + + ) : ( + + ) + ) : ( + {t('Fixed markup (%price% premium)', { price: '10%' })} + )} + + - - - + {typeIndex !== 0 && } + {typeIndex !== 0 && } + {typeIndex !== 0 && } ) } diff --git a/src/views/Bazaar/components/ShopList.tsx b/src/views/Bazaar/components/ShopList.tsx index d39180a..c69355b 100644 --- a/src/views/Bazaar/components/ShopList.tsx +++ b/src/views/Bazaar/components/ShopList.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react' -import styled, { keyframes } from 'styled-components' +import styled from 'styled-components' import { useTranslation } from 'contexts/Localization' import { Flex, Image } from '@pancakeswap/uikit' @@ -49,14 +49,25 @@ const ShopItem = styled(Flex)` text-align: center; transform: rotate(-45deg); position: relative; - padding: 2px 0; - left: -36px; - top: 14px; - width: 121px; + padding: 8px 0; + left: -33px; + top: 26px; + width: 150px; color: white; box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1); letter-spacing: 1px; - font-size: 12px; + font-size: 14px; + + ${({ theme }) => theme.mediaQueries.xs} { + padding: 2px 0; + left: -43px; + top: 16px; + } + ${({ theme }) => theme.mediaQueries.lg} { + padding: 8px 0; + left: -33px; + top: 26px; + } } & > .epic { background: linear-gradient(-90deg, #efea48 0%, #f32121 100%); @@ -72,10 +83,6 @@ const ShopItem = styled(Flex)` } } ` -const ItemText = styled(Flex)` - padding: 25px 0; - justify-content: center; -` interface ShopListItemProps { item?: Detail @@ -130,10 +137,6 @@ const ShopList: React.FC = ({ {grade === 'RARE' &&
{t('uncommon')}
} {grade === 'NORMAL' &&
{t('common')}
} - {/* {item.type === 1 && } - {item.type === 2 && } - {item.type === 3 && } - {item.type === 4 && } */} {img ? ( ) : ( diff --git a/src/views/Bazaar/components/Transaction.tsx b/src/views/Bazaar/components/Transaction.tsx index 529d939..be1ca31 100644 --- a/src/views/Bazaar/components/Transaction.tsx +++ b/src/views/Bazaar/components/Transaction.tsx @@ -50,23 +50,23 @@ const Transaction: React.FC = () => { return ( - 1.000000 + 0 {t('The total volume')} - 1.000000 + 0 {t('The total number of transactions')} - 1.000000 + 0 {t('Total number of auctions')} - 1.000000 - {t('Total auction commission')} + 0 + {t('Total transaction rebate')} ) diff --git a/src/views/Bazaar/components/TransactionRecord.tsx b/src/views/Bazaar/components/TransactionRecord.tsx index 60a2791..ee3bbef 100644 --- a/src/views/Bazaar/components/TransactionRecord.tsx +++ b/src/views/Bazaar/components/TransactionRecord.tsx @@ -1,10 +1,16 @@ -import React, { useState } from 'react' +import React, { useState, useEffect } from 'react' +import dayjs from 'dayjs' import styled from 'styled-components' +import Pagination from '@mui/material/Pagination' import { Text, Button, Image, Flex } from '@pancakeswap/uikit' import { useTranslation } from 'contexts/Localization' +import Empty from 'components/Empty' +import { useGetPurchaseRecord, useGetNftDetail } from '../hooks' interface TransactionRecordProps { onDismiss?: () => void + activeIndex?: number + recordDetail?: (v) => void } const FlexMain = styled.div` @@ -70,7 +76,10 @@ const ThemedItem = styled.div` color: #666666; border-top: 1px solid #e3e3e3; ` -const TableBody = styled.div`` +const TableBody = styled.div` + height: 420px; + overflow-y: auto; +` const TrFlex = styled(Flex)` padding: 10px 0; border-top: 1px solid #e3e3e3; @@ -112,7 +121,7 @@ const HashText = styled(Text)` border-bottom: 1px solid #1fc7d4; ` -const TransactionRecord: React.FC = ({ onDismiss }) => { +const TransactionRecord: React.FC = ({ onDismiss, activeIndex, recordDetail }) => { const { t } = useTranslation() const typeList = [ @@ -125,27 +134,70 @@ const TransactionRecord: React.FC = ({ onDismiss }) => { const ThemedList = [t('NFT name'), t('price'), t('Time'), t('state'), t('operation')] - const list = [ - { name: 'Cat goddess Emerald ', icon: '', price: '1', time: '2022-02-02', status: t('Has been selling'), id: '1' }, - ] + const [list, setList] = useState([]) + + const [pageNum, setPage] = useState(1) + const [count, setCount] = useState(undefined) + + const getPurchaseRecord = useGetPurchaseRecord() + const getList = async () => { + const result = await getPurchaseRecord(pageNum, 10) + setCount(getTotalPageNum(result.total, result.size)) + const arr = [] + result.content.forEach((item) => { + const obj = item + obj.priceList = [] + Object.keys(obj.price).forEach((childItem) => { + obj.priceList.push({ label: childItem, value: obj.price[childItem] }) + }) + obj.price = undefined + arr.push(obj) + }) + setList(arr) + } + + useEffect(() => { + getList() + }, [pageNum]) + + const pageChange = (event, page) => { + setPage(page) + } + const getTotalPageNum = (total, pageSize) => { + const countTotal = ((Number(total) + Number(pageSize) - 1) / Number(pageSize)).toString() + return parseInt(countTotal) + } + const goHash = (val) => { + window.open(`https://goerli.etherscan.io/tx/${val.tx}`) + } + const getNftDetail = useGetNftDetail() + const lookDetail = async (item) => { + const res = await getNftDetail(item.token, { token: item.token }) + const obj = res.info + obj.record = true + recordDetail(obj) + onDismiss() + } return ( {t('transaction record')} - - {typeList.map((item, index) => { - return ( - setTypeIndex(index)} - > - {item.label} - - ) - })} - + {activeIndex !== 0 && ( + + {typeList.map((item, index) => { + return ( + setTypeIndex(index)} + > + {item.label} + + ) + })} + + )} <> {ThemedList.map((item) => { @@ -153,24 +205,35 @@ const TransactionRecord: React.FC = ({ onDismiss }) => { })} + {list.length === 0 && } {list.map((item) => { return ( + {item.goodsName} - - {item.name} + {item.priceList.map((childItem, childIndex) => { + return ( + + <>{Number(childItem.value).toFixed(2)} + {childItem.label} + {childIndex === 0 && item.priceList.length === 2 && -} + + ) + })} - {item.price} - {item.time} - {item.status} + {dayjs(Number(item.tradeTime)).format('YYYY-MM-DD HH:mm:ss')} + {item.status ? item.status : t('success')} - {t('Detail')} - {t('deal Hash')} + lookDetail(item)}>{t('Detail')} + goHash(item)}>{t('deal Hash')} ) })} + + + ) diff --git a/src/views/Bazaar/hooks/index.ts b/src/views/Bazaar/hooks/index.ts index 4033c4a..b53707e 100644 --- a/src/views/Bazaar/hooks/index.ts +++ b/src/views/Bazaar/hooks/index.ts @@ -1,5 +1,8 @@ import { useCallback } from 'react' -import { getOfficialPage } from 'services/bazaar' +import { useBlindBox } from 'hooks/useContract' +import { getAddress, getBlindBoxAddress } from 'utils/addressHelpers' +import { getOfficialPage, getOfficialPurchase, getPurchaseRecord, getNftDetail } from 'services/bazaar' +import { ethers, Contract } from 'ethers' export const useGetOfficialPage = () => { const data = async (params) => { @@ -9,4 +12,76 @@ export const useGetOfficialPage = () => { return data } +// 授权usdt +export const useApproveUsdt = (tokenContract: Contract) => { + const handleApprove = useCallback(async () => { + try { + const tx = await tokenContract.approve(getBlindBoxAddress(), ethers.constants.MaxUint256) + const receipt = await tx.wait() + return receipt.status + } catch (e) { + return false + } + }, [tokenContract]) + + return { onApprove: handleApprove } +} +export const useApproveHcc = (tokenContract: Contract) => { + const handleApprove = useCallback(async () => { + try { + const tx = await tokenContract.approve(getBlindBoxAddress(), ethers.constants.MaxUint256) + const receipt = await tx.wait() + return receipt.status + } catch (e) { + return false + } + }, [tokenContract]) + + return { onApprove: handleApprove } +} +// 购买 +// export const useGetPurchase = () => { +// const data = async (id, params) => { +// const result = await getOfficialPurchase(id, params) +// console.log(result) +// return result +// } +// return data +// } + +// 交易记录 +export const useGetPurchaseRecord = () => { + const data = async (page, size) => { + const result = await getPurchaseRecord({ page, size }) + console.log(result) + return result + } + return data +} + +// 购买合约 +export const useBuyTransaction = () => { + const blindBox = useBlindBox() + const address = getBlindBoxAddress() + const transaction = async (id, params) => { + const result = await getOfficialPurchase(id, params) + const { num, hccPrice, otherPaymentPrice, timestamp, code, type, sign } = result + const mintParams = [address, num, hccPrice, otherPaymentPrice, timestamp, code, type, sign] + const res = await blindBox.mint(...mintParams) + console.log(res) + return res + } + return transaction +} + +// 交易详情 +export const useGetNftDetail = () => { + const data = async (token, params) => { + const result = await getNftDetail(token, params) + console.log(result) + return result + } + return data +} + export default useGetOfficialPage diff --git a/src/views/BlindBox/component/Header.tsx b/src/views/BlindBox/component/Header.tsx index 4d4da83..87b7c3e 100644 --- a/src/views/BlindBox/component/Header.tsx +++ b/src/views/BlindBox/component/Header.tsx @@ -1,5 +1,7 @@ -import React, { useEffect } from 'react' +import React, { useEffect, useState } from 'react' +import dayjs from 'dayjs' import styled from 'styled-components' +import useRefresh from 'hooks/useRefresh' import { useTranslation } from 'contexts/Localization' import { Flex, Text, useModal } from '@pancakeswap/uikit' import SeriesDetail from './SeriesDetail' @@ -19,18 +21,8 @@ interface DetailProp { interface OperationProp { detail: DetailProp + totalNumber?: number } -// interface listProps { -// beginTime?: string -// coverResource: coverResourceProps -// endTime?: string -// id?: string -// name?: string -// price: priceProps -// purchased?: string | number -// total?: string | number -// type?: string -// } interface coverResourceProps { path?: string url?: string @@ -72,17 +64,109 @@ const TipFlex = styled(Flex)` cursor: pointer; ` -const Header: React.FC = ({ detail }) => { +const Header: React.FC = ({ detail, totalNumber }) => { const { t } = useTranslation() const [onSeriesDetail] = useModal() + const [countDown, setCountDown] = useState('') + + const [quantitativeType, setQuantitativeType] = useState(0) + const [timeLimit, setTimeLimit] = useState(0) + const { fastRefresh } = useRefresh() + useEffect(() => { + if (detail.type === 'QUANTITATIVE') { + const num = totalNumber + switch (new Date(detail.beginTime).getTime() > new Date().getTime()) { + case true: + setQuantitativeType(0) + break + case false: + if (num > 0) { + setQuantitativeType(1) + } else { + setQuantitativeType(2) + } + break + default: + setQuantitativeType(0) + } + } else { + const date2 = dayjs(new Date(detail.endTime).getTime()).diff(dayjs()) + switch (new Date(detail.beginTime).getTime() > new Date().getTime()) { + case true: + setTimeLimit(0) + break + case false: + if (date2 > 0) { + setTimeLimit(1) + } else { + setTimeLimit(2) + } + break + default: + setTimeLimit(1) + } + } + }, [fastRefresh]) + + const countDownFun = (date) => { + const date1 = dayjs() + const date2 = dayjs(date) + const time = date2.diff(date1) + if (time > 0) { + const hour = Math.floor(time / (1000 * 60 * 60)) + const minute = Math.floor((time / (1000 * 60)) % 60) + const second = Math.round((time / 1000) % 60) + setCountDown(`${hour}:${minute}:${second}`) + } else { + setTimeLimit(2) + } + } + useEffect(() => { + const date2 = dayjs(new Date(detail.endTime).getTime()).diff(dayjs()) + if (date2 > 0) { + setTimeout(() => { + const time = new Date(detail.endTime).getTime() - 1 + countDownFun(time) + }, 1000) + } else { + setTimeLimit(2) + } + }, [countDown]) + + const getTime = (date) => { + const date1 = dayjs() + const date2 = dayjs(date) + const time = date2.diff(date1) + return time + } return ( -
- - {t('time remaining')} - {t('%hour%hour', { hour: 7 })} - +
+ {detail.type === 'QUANTITATIVE' && ( + <> + {quantitativeType === 0 && {t('have not started')}} + {quantitativeType === 1 && ( + + {t('remaining quantity')} + {totalNumber} + + )} + {quantitativeType === 2 && {t('finished')}} + + )} + {detail.type === 'TIME_LIMIT' && ( + <> + {timeLimit === 0 && {t('have not started')}} + {timeLimit === 1 && ( + + {t('time remaining')} + {countDown} + + )} + {timeLimit === 2 && {t('finished')}} + + )} ? ) diff --git a/src/views/BlindBox/component/Operation.tsx b/src/views/BlindBox/component/Operation.tsx index 2e5dc4d..b6b0164 100644 --- a/src/views/BlindBox/component/Operation.tsx +++ b/src/views/BlindBox/component/Operation.tsx @@ -1,20 +1,17 @@ -import React, { useEffect, useState } from 'react' +import React from 'react' import styled from 'styled-components' -import { formatTimeNumber } from 'utils/formatBalance' -import BigNumber from 'bignumber.js' import { useTranslation } from 'contexts/Localization' import { Flex, Text } from '@pancakeswap/uikit' -import { TOKEN_SYMBOL } from 'config/index' import { ListProp } from 'types/blindBox' import StepCom from './StepCom' interface OperationProp { detail: ListProp + stepNum: number getNum?: (v) => void } const DetailDiv = styled.div` - margin-top: -100px; padding-bottom: 45px; ` @@ -34,13 +31,12 @@ const HeaderText = styled(Text)` margin: 0 5px 0 10px; ` -const Operation: React.FC = ({ detail, getNum }) => { +const Operation: React.FC = ({ detail, getNum, stepNum }) => { const { t } = useTranslation() const getStepValue = (v) => { getNum(v) } - return ( @@ -53,7 +49,7 @@ const Operation: React.FC = ({ detail, getNum }) => { {detail.priceList.map((item, index) => { return ( - {formatTimeNumber(item.value)} + {item.value} {item.label} {index === 0 && detail.priceList.length === 2 && -} @@ -63,7 +59,7 @@ const Operation: React.FC = ({ detail, getNum }) => { {t('quantity')} - getStepValue(v)} /> + getStepValue(v)} /> ) diff --git a/src/views/BlindBox/component/SeriesDetail.tsx b/src/views/BlindBox/component/SeriesDetail.tsx index da3a1c4..d8f8125 100644 --- a/src/views/BlindBox/component/SeriesDetail.tsx +++ b/src/views/BlindBox/component/SeriesDetail.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react' +import dayjs from 'dayjs' import styled from 'styled-components' import { useTranslation } from 'contexts/Localization' import { Modal, Flex, Text, Image } from '@pancakeswap/uikit' @@ -89,13 +90,17 @@ const SeriesDetail: React.FC = ({ name, value, onDismiss, deta const [list, setList] = useState([]) const [totalNum, setTotal] = useState(0) const [size, setSize] = useState(0) + const [typeStatus, setType] = useState('') + const [countDown, setCountDown] = useState('') + const [timeLimit, setTimeLimit] = useState(0) const getDetail = useGetBoxDetail() const getData = async () => { - const { contentList, total } = await getDetail(detail.id) + const { contentList, total, type } = await getDetail(detail.id) setSize(contentList.length) setTotal(total) + setType(type) const dataList = [] - contentList.forEach((item, index) => { + contentList.forEach((item) => { const has = dataList.findIndex((o) => o.grade === item.grade) if (has === -1) { dataList.push({ @@ -111,13 +116,69 @@ const SeriesDetail: React.FC = ({ name, value, onDismiss, deta useEffect(() => { getData() - }, []) + if (detail.type === 'TIME_LIMIT') { + const date2 = dayjs(new Date(detail.endTime).getTime()).diff(dayjs()) + switch (new Date(detail.beginTime).getTime() > new Date().getTime()) { + case true: + setTimeLimit(0) + break + case false: + if (date2 > 0) { + setTimeLimit(1) + } else { + setTimeLimit(2) + } + break + default: + setTimeLimit(1) + } + } + }, [detail]) + + const countDownFun = (date) => { + const date1 = dayjs() + const date2 = dayjs(date) + const time = date2.diff(date1) + if (time > 0) { + const hour = Math.floor(time / (1000 * 60 * 60)) + const minute = Math.floor((time / (1000 * 60)) % 60) + const second = Math.round((time / 1000) % 60) + setCountDown(`${hour}:${minute}:${second}`) + } else { + setTimeLimit(2) + } + } + useEffect(() => { + const date2 = dayjs(new Date(detail.endTime).getTime()).diff(dayjs()) + if (date2 > 0) { + setTimeout(() => { + const time = new Date(detail.endTime).getTime() - 1 + countDownFun(time) + }, 1000) + } else { + setTimeLimit(2) + } + }, [countDown]) + + const getTime = (date) => { + const date1 = dayjs() + const date2 = dayjs(date) + const time = date2.diff(date1) + return time + } return (
- {t('limit the quantity of')} - {totalNum} + {detail.type === 'QUANTITATIVE' ? t('limit the quantity of') : t('time limit')} + {detail.type === 'QUANTITATIVE' && {totalNum}} + {detail.type === 'TIME_LIMIT' && ( + <> + {timeLimit === 0 && {t('have not started')}} + {timeLimit === 1 && {countDown}} + {timeLimit === 2 && {t('finished')}} + + )} @@ -134,7 +195,7 @@ const SeriesDetail: React.FC = ({ name, value, onDismiss, deta {item.grade === 'NORMAL' && {t('common')}} {item.list.map((childItem) => { - return + return })} diff --git a/src/views/BlindBox/component/ShopList.tsx b/src/views/BlindBox/component/ShopList.tsx index ad70cb2..31dd61d 100644 --- a/src/views/BlindBox/component/ShopList.tsx +++ b/src/views/BlindBox/component/ShopList.tsx @@ -1,13 +1,12 @@ -import React, { useState, useEffect } from 'react' -import styled, { keyframes } from 'styled-components' +import React from 'react' +import styled from 'styled-components' import { useTranslation } from 'contexts/Localization' import { Flex, Image } from '@pancakeswap/uikit' +import { coverResourceProps } from 'types/blindBox' const ShopItem = styled.div` - /* height: 358px; */ + width: 211px; border-radius: 20px; - /* background: rgba(255, 255, 255, 0.39); - box-shadow: 0px 1px 8px rgba(0, 0, 0, 0.15); */ position: relative; overflow: hidden; @@ -45,16 +44,16 @@ const ShopItem = styled.div` } } & > .epic { - background: linear-gradient(180deg, #efea48 0%, #f32121 100%); + background: linear-gradient(110deg, #efea48 0%, #f32121 100%); } & > .legend { - background: linear-gradient(180deg, #4b84f5 0%, #bc21f3 100%); + background: linear-gradient(110deg, #4b84f5 0%, #bc21f3 100%); } & > .uncommon { - background: linear-gradient(180deg, #3dffec 0%, #24bf52 100%); + background: linear-gradient(110deg, #3dffec 0%, #24bf52 100%); } & > .common { - background: linear-gradient(180deg, #b5e9f3 0%, #1195d9 100%); + background: linear-gradient(110deg, #b5e9f3 0%, #1195d9 100%); } } ` @@ -65,15 +64,17 @@ const ItemText = styled(Flex)` color: #707070; text-align: center; ` -const ImageType = styled.img` +const ImageType = styled(Image)` border-radius: 20px; ` const ProbabilityFlex = styled(Flex)` + width: 100%; font-size: 26px; justify-content: center; color: #3cbbcc; ` const ProbabilityTitle = styled(Flex)` + width: 100%; font-size: 18px; color: #666666; margin-top: 5px; @@ -83,16 +84,19 @@ const ProbabilityTitle = styled(Flex)` interface ShopListItemProps { item?: Detail grade?: string + type?: string } interface Detail { goodsId?: string grade?: string id?: string + num?: string name?: string proportion?: number | string + coverResource?: coverResourceProps } -const ShopList: React.FC = ({ item, grade }) => { +const ShopList: React.FC = ({ item, grade, type }) => { const { t } = useTranslation() return ( @@ -103,14 +107,10 @@ const ShopList: React.FC = ({ item, grade }) => { {grade === 'RARE' &&
{t('uncommon')}
} {grade === 'NORMAL' &&
{t('common')}
}
- {/* */} - {grade === 'EPIC' && } - {grade === 'LEGEND' && } - {grade === 'RARE' && } - {grade === 'NORMAL' && } + {item.name} - {item.proportion}% - {t('The rate of')} + {type === 'QUANTITATIVE' ? item.num : `${item.proportion}%`} + {type === 'QUANTITATIVE' ? t('quantity') : t('The rate of')} ) } diff --git a/src/views/BlindBox/component/StepCom.tsx b/src/views/BlindBox/component/StepCom.tsx index 362c757..8a73022 100644 --- a/src/views/BlindBox/component/StepCom.tsx +++ b/src/views/BlindBox/component/StepCom.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import styled from 'styled-components' import { Flex, Text, Button } from '@pancakeswap/uikit' @@ -32,7 +32,11 @@ interface StepProp { } const StepCom: React.FC = ({ number, max = 5, value }) => { - const [valNumber, setInputState] = useState(number) + const [valNumber, setInputState] = useState(1) + + useEffect(() => { + setInputState(number) + }, [number]) const onChange = (type) => { let num = valNumber @@ -42,7 +46,7 @@ const StepCom: React.FC = ({ number, max = 5, value }) => { value(num) setInputState(num) } else { - if (valNumber === 0) return + if (valNumber === 1) return num -= 1 value(num) setInputState(num) diff --git a/src/views/BlindBox/hooks/index.ts b/src/views/BlindBox/hooks/index.ts index 3890491..2f5489f 100644 --- a/src/views/BlindBox/hooks/index.ts +++ b/src/views/BlindBox/hooks/index.ts @@ -1,16 +1,66 @@ import { useCallback } from 'react' -import { useHccGiftNft } from 'hooks/useContract' +import { useBlindBox } from 'hooks/useContract' import blindBox from 'config/abi/blindBox.json' +import useTokenBalance from 'hooks/useTokenBalance' import { getAddress, getBlindBoxAddress } from 'utils/addressHelpers' import multicall from 'utils/multicall' import { ethers, Contract } from 'ethers' +import tokensList from 'config/constants/tokens' +import useToast from 'hooks/useToast' +import { useTranslation } from 'contexts/Localization' import { getBoxPage, getBoxDetail, getPurchase } from 'services/blindBox' +import { getBalanceNumber, getDecimalAmountNumber } from 'utils/formatBalance' + +export const useCheckTokenBalance = () => { + const { balance: usdtTokenBalance } = useTokenBalance(getAddress(tokensList.usdt.address)) + const { toastWarning } = useToast() + const { t } = useTranslation() + const onCheck = useCallback( + (usdtAmount) => { + if (getBalanceNumber(usdtTokenBalance) <= usdtAmount) { + toastWarning(t('Insufficient Balance')) + return false + } + return true + }, + [usdtTokenBalance], + ) + return [onCheck] +} + +export const useCheckHccTokenBalance = () => { + const { balance: hccTokenBalance } = useTokenBalance(getAddress(tokensList.hcc.address)) + const { toastWarning } = useToast() + const { t } = useTranslation() + const onCheck = useCallback( + (hccAmount) => { + if (getBalanceNumber(hccTokenBalance) <= hccAmount) { + toastWarning(t('Insufficient Balance')) + return false + } + return true + }, + [hccTokenBalance], + ) + return [onCheck] +} // nft盒子 export const useGetList = () => { const data = async (page, size) => { - const result = await getBoxPage({ page, size }) - return result + const { content } = await getBoxPage({ page, size }) + const arr = content.map((item) => { + const obj = item + obj.num = 1 + obj.priceList = [] + + Object.keys(obj.price).forEach((childItem) => { + obj.priceList.push({ label: childItem, value: obj.price[childItem] }) + }) + obj.price = undefined + return obj + }) + return arr } return data } @@ -37,7 +87,7 @@ export const useApproveUsdt = (tokenContract: Contract) => { return { onApprove: handleApprove } } -export const useApproveHccGiftNft = (tokenContract: Contract) => { +export const useApproveHcc = (tokenContract: Contract) => { const handleApprove = useCallback(async () => { try { const tx = await tokenContract.approve(getBlindBoxAddress(), ethers.constants.MaxUint256) @@ -51,13 +101,20 @@ export const useApproveHccGiftNft = (tokenContract: Contract) => { return { onApprove: handleApprove } } -// 购买 -export const useGetPurchase = () => { - const data = async (id) => { - const result = await getPurchase(id) - return result +// 购买合约 +export const useBuyTransaction = () => { + const blindBoxFun = useBlindBox() + const address = getBlindBoxAddress() + const transaction = async (id, params) => { + const result = await getPurchase(id, params) + const { num, hccPrice, otherPaymentPrice, timestamp, code, type, sign, key } = result + const mintParams = [address, num, hccPrice, otherPaymentPrice, timestamp, code, type, sign] + const res = await blindBoxFun.mint(...mintParams) + res.key = key + res.code = code + return res } - return data + return transaction } export default useGetList diff --git a/src/views/BlindBox/index.tsx b/src/views/BlindBox/index.tsx index 0021848..615eef3 100644 --- a/src/views/BlindBox/index.tsx +++ b/src/views/BlindBox/index.tsx @@ -1,20 +1,31 @@ import React, { useEffect, useState } from 'react' +import dayjs from 'dayjs' import styled from 'styled-components' import { useTranslation } from 'contexts/Localization' import { useAccount } from 'state/userInfo/hooks' -import { getAddress } from 'utils/addressHelpers' +import { getAddress, getBlindBoxAddress } from 'utils/addressHelpers' import { fetchBlindBoxUserAllowances } from 'state/blindBox' import { useERC20 } from 'hooks/useContract' import UnlockButton from 'components/UnlockButton' import SwiperCore, { Keyboard, Mousewheel, Pagination } from 'swiper' import { Swiper, SwiperSlide } from 'swiper/react' -import { Card, Text, Flex, Image, Button } from '@pancakeswap/uikit' +import { Flex, Image, Button } from '@pancakeswap/uikit' +import { checkBuyResult } from 'services/blindBox' +import useRefresh from 'hooks/useRefresh' +import useToast from 'hooks/useToast' import { UnOpenModel } from 'components/Modal' import tokens from 'config/constants/tokens' import { ListProp } from 'types/blindBox' import Header from './component/Header' import Operation from './component/Operation' -import { useGetList, useApproveHccGiftNft } from './hooks' +import { + useGetList, + useBuyTransaction, + useApproveHcc, + useApproveUsdt, + useCheckHccTokenBalance, + useCheckTokenBalance, +} from './hooks' import 'swiper/swiper.min.css' import 'swiper/components/pagination/pagination.min.css' @@ -29,7 +40,7 @@ const MainFlex = styled(Flex)` ` const SwiperDiv = styled(Swiper)` - height: 730px; + height: 750px; position: relative; & > .swiper-wrapper > .swiper-slide > div > div { background: transparent; @@ -38,15 +49,16 @@ const SwiperDiv = styled(Swiper)` .swiper-pagination-custom, .swiper-pagination-fraction { /* position: absolute; */ - bottom: 130px; + bottom: 80px; } & > .swiper-pagination { - bottom: 130px !important; + display: ${(props) => props.color}; + bottom: 80px !important; ${({ theme }) => theme.mediaQueries.xs} { - bottom: 260px !important; + bottom: 140px !important; } ${({ theme }) => theme.mediaQueries.lg} { - bottom: 130px !important; + bottom: 80px !important; } & > .swiper-pagination-bullet { background: #fff; @@ -144,29 +156,46 @@ const BuyButton = styled(Button)` } ` const AuthorizationBtn = styled(Button)` - width: 40%; - margin: 20px auto 0px auto; - border-radius: 50px; - background: linear-gradient(180deg, #7be0fc 0%, #ac7bf1 100%); - border: none; + width: 500px; + height: 45px; + background: linear-gradient(180deg, #7be0fc 0%, #9961f0 100%); + opacity: 1; + border-radius: 23px; + margin-top: 10px; + ${({ theme }) => theme.mediaQueries.xs} { + width: 350px; + } + ${({ theme }) => theme.mediaQueries.lg} { + width: 500px; + } ` const BlindBox: React.FC = () => { const { t } = useTranslation() + const [txId, setTxId] = useState() + const [keyId, setKeyId] = useState() + const [code, setCode] = useState() const [allowanceList, setAllowanceList] = useState({ usdt: 0, hcc: 0 }) + const [buyVisible, setBuyVisible] = useState(false) + const { toastSuccess, toastError } = useToast() + const { fastRefresh } = useRefresh() const [loading, setLoading] = useState(false) const account = useAccount() const [blindBoxList, setBlindBoxList] = useState() - const [buyNum, setBuyNum] = useState(0) + const [buyNum, setBuyNum] = useState(1) const usdtContract = useERC20(getAddress(tokens.usdt.address)) const hccContract = useERC20(getAddress(tokens.hcc.address)) - const { onApprove: onUsdtApprove } = useApproveHccGiftNft(usdtContract) - const { onApprove: onHccApprove } = useApproveHccGiftNft(hccContract) + const { onApprove: onUsdtApprove } = useApproveHcc(usdtContract) + const { onApprove: onHccApprove } = useApproveHcc(hccContract) const getAllowances = async () => { const allowances = await fetchBlindBoxUserAllowances(account) + if (allowances.usdt && allowances.hcc) { + setBuyVisible(true) + } + setAllowanceList({ usdt: allowances.usdt, hcc: allowances.hcc, @@ -187,39 +216,78 @@ const BlindBox: React.FC = () => { const getList = useGetList() const getData = async () => { - const { content } = await getList(1, 10) - const arr = [] - content.forEach((item) => { - const obj = item - obj.num = 0 - obj.priceList = [] - Object.keys(obj.price).forEach((childItem) => { - obj.priceList.push({ label: childItem, value: obj.price[childItem] }) - }) - obj.price = undefined - arr.push(obj) - }) + const arr = await getList(1, 10) setBlindBoxList(arr) } + useEffect(() => { + if (account) { + getAllowances() + } + }, [account]) + + const buyTransaction = useBuyTransaction() + const [onCheckBalanceHcc] = useCheckHccTokenBalance() + const [onCheckBalanceUsdt] = useCheckTokenBalance() + const handleBuy = async (val) => { + if (buyNum === 0) { + toastError(t('Please select quantity')) + return + } + let HccBalance = true + val.priceList && + val.priceList.forEach((item) => { + if (item.label === 'HCC') { + HccBalance = onCheckBalanceHcc(Number(item.value)) + } else { + HccBalance = onCheckBalanceUsdt(Number(item.value)) + } + }) + if (!HccBalance) { + return + } + setLoading(true) + const res = await buyTransaction(val.id, { + id: val.id, + num: buyNum, + }) + setTxId(res.hash) + setKeyId(res.key) + setCode(res.code) + } + const getTransactionResult = async () => { + const res = await checkBuyResult({ tx: txId, key: keyId, boxId: code }) + if (res) { + setLoading(false) + setTxId(undefined) + setKeyId(undefined) + setCode(undefined) + setBuyNum(1) + toastSuccess(t('purchase succeeds')) + getData() + } + } + useEffect(() => { + if (txId && loading) { + getTransactionResult() + } + }, [fastRefresh]) useEffect(() => { getData() }, []) - - const handleBuy = () => { - console.log(buyNum) + const swiperChange = () => { + setBuyNum(1) + } + const getTime = (date) => { + const date1 = dayjs() + const date2 = dayjs(date) + const time = date2.diff(date1) + return time } - return ( - - {/* */} - + const renderContent = (): JSX.Element => { + return ( + <> {blindBoxList?.map((item) => { return ( @@ -232,37 +300,73 @@ const BlindBox: React.FC = () => {
{t('time limit')}
)}
-
- - setBuyNum(v)} /> +
+ + + + + setBuyNum(v)} /> - {account ? {t('Buy It Now')} : } - {!allowanceList.usdt && ( - { - handleApprove(onUsdtApprove) - }} + {!account && } + + {buyVisible && account && ( + new Date().getTime() || + (item.type === 'TIME_LIMIT' && getTime(item.endTime) <= 0) || + (item.type === 'QUANTITATIVE' && Number(item.total) - Number(item.purchased) <= 0) + } + onClick={() => handleBuy(item)} > - {t('Approve %coin% Contract', { coin: 'USDT' })} - + {t('Buy It Now')} + )} - {!allowanceList.hcc && ( - { - handleApprove(onHccApprove) - }} - > - {t('Approve %coin% Contract', { coin: 'HCC' })} - - )} + + {!allowanceList.usdt && ( + { + handleApprove(onUsdtApprove) + }} + > + {t('Approve %coin% Contract', { coin: 'USDT' })} + + )} + + {!allowanceList.hcc && ( + { + handleApprove(onHccApprove) + }} + > + {t('Approve %coin% Contract', { coin: 'HCC' })} + + )} + ) })} + + ) + } + + return ( + + {/* */} + + {renderContent()} ) diff --git a/src/views/Board/components/BoardCard/FlexText.tsx b/src/views/Board/components/BoardCard/FlexText.tsx index 758e21a..b466b75 100644 --- a/src/views/Board/components/BoardCard/FlexText.tsx +++ b/src/views/Board/components/BoardCard/FlexText.tsx @@ -5,11 +5,12 @@ import { Flex, Text } from '@pancakeswap/uikit' interface FlexProp { name?: string | number value?: string | number + marginBottom?: string } -const FlexText: React.FC = ({ name, value }) => { +const FlexText: React.FC = ({ name, value, marginBottom = '0px' }) => { return ( - + {name} {value} diff --git a/src/views/Board/components/BoardCard/HolderPoolBoardCard.tsx b/src/views/Board/components/BoardCard/HolderPoolBoardCard.tsx new file mode 100644 index 0000000..08d2664 --- /dev/null +++ b/src/views/Board/components/BoardCard/HolderPoolBoardCard.tsx @@ -0,0 +1,101 @@ +import React from 'react' +import dayjs from 'dayjs' +import BigNumber from 'bignumber.js' +import styled from 'styled-components' +import { Flex, Text, Button } from '@pancakeswap/uikit' +import { provider as ProviderType } from 'web3-core' +import { getAddress, getHolderPoolAddress } from 'utils/addressHelpers' +import { usePriceHccUsdt } from 'state/hooks' +import { getBalanceAmount } from 'utils/formatBalance' +import { BIG_ZERO } from 'utils/bigNumber' +import { useTranslation } from 'contexts/Localization' +import { useWithdraw } from '../../hooks/useHolderPoolBoard' +import CardHeading from './CardHeading' +import FlexText from './FlexText' + +const FCard = styled.div` + align-self: baseline; + background: ${(props) => props.theme.card.background}; + border-radius: 32px; + box-shadow: 0px 2px 30px 0px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + position: relative; + text-align: center; + min-height: 410px; +` +const ContentDiv = styled.div` + padding: 0 20px; +` +const InfoDiv = styled(Flex)` + flex-direction: column; + justify-content: space-between; + margin-bottom: 10px; +` + +const PriceCoin = styled.div` + text-align: right; +` + +interface NodeCardProps { + board: any + removed: boolean + provider?: ProviderType + account?: string + boardsData?: any +} + +const HolderPoolBoardCard: React.FC = ({ board, account, boardsData }) => { + const { t } = useTranslation() + const hccPriceUsdt = usePriceHccUsdt() + // const rawEarningsBalance = account ? getBalanceAmount(new BigNumber(board.estimatedProfit)).toNumber() : BIG_ZERO + // const displayBalance = rawEarningsBalance.toFixed(3, BigNumber.ROUND_DOWN) + + const withdraw = useWithdraw() + const harvest = async () => { + const res = await withdraw(account) + console.log(res) + } + + return ( + + + + {account && ( + + + + + + {t('Unclaimed income')} + + + + {board.estimatedProfit} + + + {hccPriceUsdt ? (hccPriceUsdt * board.estimatedProfit).toFixed(3) : 0} USDT + + + + + )} + {/* disabled={Number(displayBalance) <= 0} */} + + + + ) +} + +export default HolderPoolBoardCard diff --git a/src/views/Board/hooks/useHolderPoolBoard.ts b/src/views/Board/hooks/useHolderPoolBoard.ts new file mode 100644 index 0000000..35ec559 --- /dev/null +++ b/src/views/Board/hooks/useHolderPoolBoard.ts @@ -0,0 +1,54 @@ +import { useCallback } from 'react' +import holderPoolABI from 'config/abi/holderPool.json' +import { getHolderPoolAddress } from 'utils/addressHelpers' +import { unstakeBoard, unstakeForceBoard } from 'utils/calls' +import { useHolderPool } from 'hooks/useContract' +import { + getBalanceNumber, + getDecimalAmount, + formatDivNumber, + getDecimalAmountNumber, + formatTimeNumber, +} from 'utils/formatBalance' +import BigNumber from 'bignumber.js' +import multicall from 'utils/multicall' + +// 获取信息 +export const useUserInfo = () => { + const transaction = async (account) => { + const holderPoolAddress = getHolderPoolAddress() + const calls = [ + { + address: holderPoolAddress, + name: 'userInfo', + params: [account], + }, + { + address: holderPoolAddress, + name: 'pendingHCC', + params: [account], + }, + ] + + const [userInfo, estimatedProfit] = await multicall(holderPoolABI, calls) + return { + amount: new BigNumber(userInfo?.amount._hex).toNumber(), + receiveReward: getBalanceNumber(userInfo?.receiveReward._hex), + rewardDebt: new BigNumber(userInfo?.rewardDebt._hex).toNumber(), + estimatedProfit: getBalanceNumber(estimatedProfit), + } + } + return transaction +} + +export const useWithdraw = () => { + const holderPool = useHolderPool() + const transaction = async (account) => { + const res = await holderPool.withdrawHCC(account) + const receipt = await res.wait() + return receipt.status + } + return transaction +} + +export default useUserInfo diff --git a/src/views/Board/index.tsx b/src/views/Board/index.tsx index 06f852e..2d681bc 100644 --- a/src/views/Board/index.tsx +++ b/src/views/Board/index.tsx @@ -2,6 +2,7 @@ import BigNumber from 'bignumber.js' import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react' import { Route, useRouteMatch, useLocation } from 'react-router-dom' import { useDispatch } from 'react-redux' +import { Address } from 'config/constants/types' import boardsConfig from 'config/constants/boards' import tokens from 'config/constants/tokens' import { getAddress, getBoardAddress } from 'utils/addressHelpers' @@ -21,9 +22,21 @@ import { fetchBoardUserDataAsync, fetchBoardsPublicDataAsync } from 'state/actio import { useAccount } from 'state/userInfo/hooks' import { useTranslation } from 'contexts/Localization' import { UnOpenModel } from 'components/Modal' +import { useUserInfo } from './hooks/useHolderPoolBoard' import BoardCard from './components/BoardCard/BoardCard' +import HolderPoolBoardCard from './components/BoardCard/HolderPoolBoardCard' import HeaderItem from './components/HeaderItem' +interface userInfoProps { + pid?: number + name?: string + amount?: number + receiveReward?: number + rewardDebt?: number + tokenAddresses?: Address + estimatedProfit?: number +} + const PageContent = styled.div` background-image: url('/images/recommend/bg.svg'); background-repeat: no-repeat; @@ -75,6 +88,8 @@ const Boards: React.FC = () => { const [boardsDataList, setBoardsDataList] = useState([]) + const [nftHolder, setNftHolder] = useState({}) + // 获取分红总额 const fetchBoardShares = async () => { const boardsData = await Promise.all( @@ -117,7 +132,21 @@ const Boards: React.FC = () => { setTotalAmount(total) setShareOutBonus(totalReward) } + const userInfo = useUserInfo() + const getUserInfo = async () => { + const res = await userInfo(account) + setNftHolder({ + pid: 3, + name: 'NFT Holder', + amount: res.amount, + receiveReward: res.receiveReward, + estimatedProfit: res.estimatedProfit, + rewardDebt: res.rewardDebt, + tokenAddresses: tokens.hcc.address, + }) + } useEffect(() => { + getUserInfo() dispatch(fetchBoardsPublicDataAsync()) fetchBoardShares() if (account) { @@ -132,6 +161,7 @@ const Boards: React.FC = () => { {boardsList.map((board, index) => ( ))} + {nftHolder.pid && } ) @@ -139,7 +169,7 @@ const Boards: React.FC = () => { return ( - + {/* */}
{t('Total capital pool')} diff --git a/src/views/Ido/components/IdoPurchaseCard.tsx b/src/views/Ido/components/IdoPurchaseCard.tsx index 3025b4d..7318a97 100644 --- a/src/views/Ido/components/IdoPurchaseCard.tsx +++ b/src/views/Ido/components/IdoPurchaseCard.tsx @@ -164,7 +164,11 @@ const ExchangeCard: React.FC = ({ status, roundDetail, time }) => { {t('Immediately change')} ) : ( - ) diff --git a/src/views/NftBox/components/AssetsInfo.tsx b/src/views/NftBox/components/AssetsInfo.tsx new file mode 100644 index 0000000..2a3d165 --- /dev/null +++ b/src/views/NftBox/components/AssetsInfo.tsx @@ -0,0 +1,79 @@ +import React, { useState, useEffect } from 'react' +import styled from 'styled-components' +import { useTranslation } from 'contexts/Localization' +import { Flex, Button, Text, Image } from '@pancakeswap/uikit' +import { getContract } from 'services/referral' +import FlexCom from './FlexText' + +interface DetailProps { + detail: Detail +} + +interface Detail { + address?: string + createdAt?: string + id?: string + info?: InfoProps + metadata?: any + token?: string + updatedAt?: string +} +interface InfoProps { + coverResource?: CoverResourceProps + grade?: string + id?: string + name?: string + price?: any + type?: string +} +interface CoverResourceProps { + path?: string + url?: string +} + +const DetailFlex = styled(Flex)` + flex-direction: column; + justify-content: space-between; + height: 380px; + background-color: #f5ffff; + border-radius: 20px; + padding: 50px 30px; +` +const TitleText = styled(Text)` + font-size: 28px; + color: #333333; + text-align: center; +` +const AssetsInfo: React.FC = ({ detail }) => { + const { t } = useTranslation() + const [link, setLink] = useState('') + + const getLinkAddress = async () => { + const data = await getContract() + setLink(data) + } + useEffect(() => { + getLinkAddress() + }, []) + + return ( + + {detail?.info.name} + {detail?.info.grade === 'EPIC' && } + {detail?.info.grade === 'LEGEND' && } + {detail?.info.grade === 'RARE' && } + {detail?.info.grade === 'NORMAL' && } + {detail?.info.type === 'PROPS' && } + {detail?.info.type === 'GIVING' && } + + + + + + ) +} +export default AssetsInfo diff --git a/src/views/NftBox/components/Box.tsx b/src/views/NftBox/components/Box.tsx index aa8a4b1..e5a9f64 100644 --- a/src/views/NftBox/components/Box.tsx +++ b/src/views/NftBox/components/Box.tsx @@ -5,25 +5,12 @@ import { useTranslation } from 'contexts/Localization' import { Heading, Flex } from '@pancakeswap/uikit' import { useAccount } from 'state/userInfo/hooks' import useRefresh from 'hooks/useRefresh' +import Empty from 'components/Empty' +import { useGetSelfPage } from '../hooks' import ShopList from './ShopList' -import NumMain from './NumMain' - -const aaa = keyframes` -0% { - background-position: 0 50%; -} -50% { - background-position: 100% 50%; -} -100% { - background-position: 0% 50%; -} -` const PageContent = styled.div` - background: rgba(255, 255, 255, 0.39); padding: 30px 0; - /* max-width: 70%; */ max-width: 1180px; margin: 0 auto; ` @@ -31,7 +18,6 @@ const StatusFlex = styled(Flex)` align-items: center; flex-wrap: wrap; margin-top: 30px; - /* padding: 0 23px; */ & > .active { background: linear-gradient(90deg, #1fd4b0 0%, #1fc9d3 100%); color: #fff; @@ -83,42 +69,53 @@ const Shop = styled.div` grid-template-columns: repeat(4, 1fr); } ` -const MainDiv = styled.div` - /* padding: 0 35px; */ -` const NftBox: React.FC = () => { const { t } = useTranslation() - const list = [ - { label: 'Cat goddess Emerald ', type: 1, id: 1 }, - { label: 'Cat goddess Emerald ', type: 2, id: 2 }, - { label: 'Cat goddess Emerald ', type: 3, id: 3 }, - { label: 'Cat goddess Emerald ', type: 4, id: 4 }, - { label: 'Cat goddess Emerald ', type: 1, id: 5 }, - ] + const [pageNum, setPage] = useState(1) + const [count, setCount] = useState(undefined) + + const [grade, setGrade] = useState('') + const [list, setList] = useState([]) const status = [ - { label: t('All'), id: '1' }, - { label: t('epic'), id: '2' }, - { label: t('legend'), id: '3' }, - { label: t('uncommon'), id: '4' }, - { label: t('common'), id: '5' }, + { label: t('All'), id: '1', grade: '' }, + { label: t('common'), id: '5', grade: 'NORMAL' }, + { label: t('uncommon'), id: '4', grade: 'RARE' }, + { label: t('epic'), id: '2', grade: 'EPIC' }, + { label: t('legend'), id: '3', grade: 'LEGEND' }, ] const [statusIndex, setStatusIndex] = useState(0) const cutStatus = (index) => { setStatusIndex(index) + setPage(1) + setGrade(status[index].grade) } const pageChange = (event, page) => { - console.log(event) - console.log(page) + setPage(page) } + const getSelfPage = useGetSelfPage() + const getData = async () => { + const params = { + page: pageNum, + size: 8, + grade, + } + const { size, total, content } = await getSelfPage(params) + setList(content) + setCount(getTotalPageNum(total, size)) + } + const getTotalPageNum = (total, pageSize) => { + const countTotal = ((Number(total) + Number(pageSize) - 1) / Number(pageSize)).toString() + return parseInt(countTotal) + } + useEffect(() => { + getData() + }, [pageNum, grade]) return ( - - - {status.map((item, index) => { return ( @@ -132,16 +129,19 @@ const NftBox: React.FC = () => { ) })} - {t('Did not have')} + {t('already owned')} {/* 商品 */} {list.map((item) => { - return + return })} - - - + {list.length > 0 && ( + + + + )} + {list.length === 0 && } ) } diff --git a/src/views/NftBox/components/DetailModal.tsx b/src/views/NftBox/components/DetailModal.tsx index cf5810a..77fa6d1 100644 --- a/src/views/NftBox/components/DetailModal.tsx +++ b/src/views/NftBox/components/DetailModal.tsx @@ -1,19 +1,15 @@ -import React from 'react' +import React, { useState, useEffect } from 'react' import styled from 'styled-components' import { useTranslation } from 'contexts/Localization' import { Modal, Flex, Text, Image, Button } from '@pancakeswap/uikit' -import FlexText from './FlexText' +import { useGetNftDetails } from '../hooks' +import AssetsInfo from './AssetsInfo' interface DetailProp { - item?: Detail + token?: string onDismiss?: () => void } -interface Detail { - label?: string - type?: number | string - id?: number | string -} const Main = styled(Modal)` width: 1050px; background-color: #fff; @@ -58,7 +54,6 @@ const MainFlex = styled(Flex)` } ` const ShopItem = styled(Flex)` - /* height: 358px; */ border-radius: 20px; justify-content: center; align-items: center; @@ -134,18 +129,6 @@ const DetailFlex = styled(Flex)` margin-top: 0px; } ` -const CardMain = styled.div` - height: 380px; - background-color: #f5ffff; - border-radius: 20px; - padding: 50px 30px; -` - -const TitleText = styled(Text)` - font-size: 28px; - color: #333333; - text-align: center; -` const SellButton = styled(Button)` height: 60px; @@ -160,55 +143,34 @@ const CenterDiv = styled.div` overflow-y: auto; ` -const DetailModal: React.FC = ({ item, onDismiss }) => { +const DetailModal: React.FC = ({ token, onDismiss }) => { const { t } = useTranslation() + const getNftDetail = useGetNftDetails() + const [detail, setDetail] = useState(undefined) - const getClassBcg = () => { - let bcg = '' - switch (item.type) { - case 1: - bcg = 'epicBcg' - break - case 2: - bcg = 'legendBcg' - break - case 3: - bcg = 'uncommonBcg' - break - case 4: - bcg = 'commonBcg' - break - default: - bcg = 'epicBcg' - } - return bcg + const getDetail = async () => { + const res = await getNftDetail(token, { token }) + setDetail(res) } + useEffect(() => { + getDetail() + }, [token]) return (
- +
- {item.type === 1 &&
{t('epic')}
} - {item.type === 2 &&
{t('legend')}
} - {item.type === 3 &&
{t('uncommon')}
} - {item.type === 4 &&
{t('common')}
} + {detail?.info.grade === 'EPIC' &&
{t('epic')}
} + {detail?.info.grade === 'LEGEND' &&
{t('legend')}
} + {detail?.info.grade === 'RARE' &&
{t('uncommon')}
} + {detail?.info.grade === 'NORMAL' &&
{t('common')}
}
- {item.type === 1 && } - {item.type === 2 && } - {item.type === 3 && } - {item.type === 4 && } +
- - Cat goddess Emerald - - - - - - + {t('Selling immediately')}
diff --git a/src/views/NftBox/components/FlexText.tsx b/src/views/NftBox/components/FlexText.tsx index 3762b43..2ed7092 100644 --- a/src/views/NftBox/components/FlexText.tsx +++ b/src/views/NftBox/components/FlexText.tsx @@ -1,29 +1,63 @@ import React from 'react' import styled from 'styled-components' -import { useTranslation } from 'contexts/Localization' -import { Text, Flex } from '@pancakeswap/uikit' +import { Flex, Text, Link } from '@pancakeswap/uikit' -interface FlexProp { - title: number | string - value: number | string - marginTop?: string - fontSize?: string - color?: string +interface FlexProps { + name: string + value: string + paddings?: string + leftColor?: string + rightColor?: string + typeLink?: string + size?: string + textColor?: string + rightSize?: string } -const MainFlex = styled(Flex)` - font-size: 14px; - color: #666666; - align-items: center; +const FlexDiv = styled(Flex)` justify-content: space-between; + align-items: center; + margin-top: 14px; + & > .linkText:hover { + color: #1fc7d4 !important; + border-bottom: 1px solid #1fc7d4 !important; + } +` +const TextLink = styled(Text)` + cursor: pointer; ` -const FlexText: React.FC = ({ title, value, marginTop = '20px', color = '#666666', fontSize = '14px' }) => { +const FlexCom: React.FC = ({ + name, + value, + paddings = '0px', + leftColor = '#666666', + rightColor = 'textSubtle', + typeLink, + size = '14px', + textColor = '#666666', + rightSize = '14px', +}) => { + const openPage = () => { + window.open(typeLink) + } + return ( - - {title} - {value} - + + {name} + {typeLink ? ( + + {value} + + ) : ( + {value} + )} + ) } -export default FlexText +export default FlexCom diff --git a/src/views/NftBox/components/ShopList.tsx b/src/views/NftBox/components/ShopList.tsx index bf6f4c7..4c8e909 100644 --- a/src/views/NftBox/components/ShopList.tsx +++ b/src/views/NftBox/components/ShopList.tsx @@ -59,28 +59,40 @@ interface ShopListItemProps { item?: Detail } interface Detail { - label?: string - type?: number | string - id?: number | string + address?: string + createdAt?: string + id?: string + info: InfoProps + token?: string + updatedAt?: string +} +interface InfoProps { + coverResource?: CoverResourceProps + grade?: string + id?: string + name?: string + price?: any + type?: string +} +interface CoverResourceProps { + path?: string + url?: string } const ShopList: React.FC = ({ item }) => { const { t } = useTranslation() - const [onDetailModal] = useModal() + const [onDetailModal] = useModal() return (
- {item.type === 1 &&
{t('epic')}
} - {item.type === 2 &&
{t('legend')}
} - {item.type === 3 &&
{t('uncommon')}
} - {item.type === 4 &&
{t('common')}
} + {item.info.grade === 'EPIC' &&
{t('epic')}
} + {item.info.grade === 'LEGEND' &&
{t('legend')}
} + {item.info.grade === 'RARE' &&
{t('uncommon')}
} + {item.info.grade === 'NORMAL' &&
{t('common')}
}
- {item.type === 1 && } - {item.type === 2 && } - {item.type === 3 && } - {item.type === 4 && } - {item.label} + + {item.info.name}
) } diff --git a/src/views/NftBox/hooks/index.ts b/src/views/NftBox/hooks/index.ts new file mode 100644 index 0000000..e3adc29 --- /dev/null +++ b/src/views/NftBox/hooks/index.ts @@ -0,0 +1,31 @@ +import { useCallback } from 'react' +import { useBlindBox } from 'hooks/useContract' +import { getAddress, getBlindBoxAddress } from 'utils/addressHelpers' +import { getSelfPage, getNftDetails } from 'services/nftBox' +import { ethers, Contract } from 'ethers' + +export const useGetSelfPage = () => { + const data = async (params) => { + const result = await getSelfPage(params) + return result + } + return data +} + +// export const useGetNftDetail = () => { +// const data = async (token, params) => { +// const result = await getNftDetail(token, params) +// console.log(result) +// return result +// } +// return data +// } + +export const useGetNftDetails = () => { + const data = async (token, params) => { + const result = await getNftDetails(token, params) + return result + } + return data +} +export default useGetSelfPage diff --git a/src/views/NftBox/index.tsx b/src/views/NftBox/index.tsx index 785bcde..dee18da 100644 --- a/src/views/NftBox/index.tsx +++ b/src/views/NftBox/index.tsx @@ -6,8 +6,10 @@ import { UnOpenModel } from 'components/Modal' import Box from './components/Box' const PageContent = styled.div` - background: rgba(255, 255, 255, 0.39); min-height: calc(100vh - 64px); + background-image: url('/images/home/bg.svg'); + background-size: cover; + background-repeat: no-repeat; /* background-image: url('/images/page/nftBox.jpg'); background-position: 50%;