diff --git a/build.zip b/build.zip new file mode 100644 index 0000000..2b5ee78 Binary files /dev/null and b/build.zip differ diff --git a/public/locales/zh-CN.json b/public/locales/zh-CN.json index 0018f49..0ab3281 100644 --- a/public/locales/zh-CN.json +++ b/public/locales/zh-CN.json @@ -1256,7 +1256,7 @@ "Selling time": "出售时间", "Add as a trading pair": "添加为交易对", "reselect NFT": "重新选择NFT", - "Auction countdown": "拍卖倒计时", + "Auction remaining time": "拍卖剩余时间", "present price%price%": "当前价格%price%", "Fixed markup (%price% premium)": "固定加价(%price%溢价)", "owner": "拥有者", @@ -1285,5 +1285,20 @@ "PROPS": "道具", "rarity": "稀有度", "success": "成功", - "Exchange closed": "兑换已结束" + "Exchange closed": "兑换已结束", + "Have they": "已挂单", + "Cancel the deity": "取消挂单", + "abortive auction": "流拍", + "out": "出局", + "For successful": "竞价成功", + "Please enter the time": "请输入时间", + "Please enter price": "请输入价格", + "hour": "小时", + "Selling rules": "售卖规则", + "1.NFT may not be traded or transferred at the time of sale": "1.NFT在出售挂单时不可进行交易或转让", + "2. After the successful sale of NFT, the platform will charge the publisher 6% of the profits as a commission fee": "2.NFT出售成功后,平台将收取发布人收益的6%作为手续费", + "order status": "订单状态", + "Asking price": "起拍价格", + "Can't be less than %num%": "不能小于%num%", + "They are in": "挂单中" } diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index 18ccea3..660f2aa 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -31,7 +31,6 @@ const Menu = (props) => { }, [userInfo]) const sign = useSignLogin() const handleLogin = async (connectorID: ConnectorNames) => { - console.log(connectorID) await login(connectorID) setHasWalletLogin(true) } diff --git a/src/config/abi/hccMarketplace.json b/src/config/abi/hccMarketplace.json new file mode 100644 index 0000000..197b30b --- /dev/null +++ b/src/config/abi/hccMarketplace.json @@ -0,0 +1,388 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "_hccToken", "type": "address" }, + { "internalType": "address", "name": "_owner", "type": "address" }, + { "internalType": "address", "name": "_teamAddress", "type": "address" }, + { "internalType": "address", "name": "_nftAddress", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint256", "name": "bidLimit", "type": "uint256" }], + "name": "ChangedBidLimit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "extensionTime", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "extensionTimeLimit", "type": "uint256" } + ], + "name": "ChangedExtensionTimeAndLimit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint256", "name": "publicationFee", "type": "uint256" }], + "name": "ChangedPublicationFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "address", "name": "teamAddress", "type": "address" }], + "name": "ChangedTeamAddress", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint256", "name": "tradeFee", "type": "uint256" }], + "name": "ChangedTradeFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "userAddress", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "relayerAddress", "type": "address" }, + { "indexed": false, "internalType": "bytes", "name": "functionSignature", "type": "bytes" } + ], + "name": "MetaTransactionExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "bytes32", "name": "id", "type": "bytes32" }, + { "indexed": true, "internalType": "address", "name": "bidder", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "latestPrice", "type": "uint256" } + ], + "name": "OrderBidFailed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "bytes32", "name": "id", "type": "bytes32" }, + { "indexed": true, "internalType": "uint256", "name": "assetId", "type": "uint256" }, + { "indexed": true, "internalType": "address", "name": "seller", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "bidder", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "latestPrice", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "expiresAt", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "timestamp", "type": "uint256" } + ], + "name": "OrderBidSuccess", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "bytes32", "name": "id", "type": "bytes32" }, + { "indexed": true, "internalType": "uint256", "name": "assetId", "type": "uint256" }, + { "indexed": true, "internalType": "address", "name": "seller", "type": "address" } + ], + "name": "OrderCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "tpe", "type": "uint256" }, + { "indexed": false, "internalType": "bytes32", "name": "id", "type": "bytes32" }, + { "indexed": true, "internalType": "uint256", "name": "assetId", "type": "uint256" }, + { "indexed": true, "internalType": "address", "name": "seller", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "price", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "expiresAt", "type": "uint256" } + ], + "name": "OrderCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "bytes32", "name": "id", "type": "bytes32" }, + { "indexed": true, "internalType": "uint256", "name": "assetId", "type": "uint256" }, + { "indexed": true, "internalType": "address", "name": "seller", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "price", "type": "uint256" }, + { "indexed": true, "internalType": "address", "name": "buyer", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "timestamp", "type": "uint256" } + ], + "name": "OrderSuccessful", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "address", "name": "account", "type": "address" }], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "address", "name": "account", "type": "address" }], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "ERC721_Interface", + "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "InterfaceId_ValidateFingerprint", + "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ORDER_TYPE_AUCTION", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ORDER_TYPE_NORMAL", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "assetId", "type": "uint256" }, + { "internalType": "uint256", "name": "bid", "type": "uint256" } + ], + "name": "bidAuctionOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "bidLimit", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "assetId", "type": "uint256" }], + "name": "cancelOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "assetId", "type": "uint256" }, + { "internalType": "uint256", "name": "priceInWei", "type": "uint256" }, + { "internalType": "uint256", "name": "expiresAt", "type": "uint256" } + ], + "name": "createAuctionOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "assetId", "type": "uint256" }, + { "internalType": "uint256", "name": "priceInWei", "type": "uint256" } + ], + "name": "createNormalOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "domainSeparator", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "userAddress", "type": "address" }, + { "internalType": "bytes", "name": "functionSignature", "type": "bytes" }, + { "internalType": "bytes32", "name": "sigR", "type": "bytes32" }, + { "internalType": "bytes32", "name": "sigS", "type": "bytes32" }, + { "internalType": "uint8", "name": "sigV", "type": "uint8" } + ], + "name": "executeMetaTransaction", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "assetId", "type": "uint256" }, + { "internalType": "uint256", "name": "price", "type": "uint256" } + ], + "name": "executeOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "extensionTime", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "extensionTimeLimit", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getChainId", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "user", "type": "address" }], + "name": "getNonce", + "outputs": [{ "internalType": "uint256", "name": "nonce", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hccToken", + "outputs": [{ "internalType": "contract ERC20Interface", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nftAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "orderMap", + "outputs": [ + { "internalType": "uint256", "name": "tpe", "type": "uint256" }, + { "internalType": "bytes32", "name": "id", "type": "bytes32" }, + { "internalType": "address", "name": "seller", "type": "address" }, + { "internalType": "uint256", "name": "price", "type": "uint256" }, + { "internalType": "address", "name": "bidder", "type": "address" }, + { "internalType": "uint256", "name": "bid", "type": "uint256" }, + { "internalType": "uint256", "name": "expiresAt", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "publicationFeeInWei", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { "internalType": "uint256", "name": "assetId", "type": "uint256" }, + { "internalType": "uint256", "name": "price", "type": "uint256" }, + { "internalType": "bytes", "name": "fingerprint", "type": "bytes" } + ], + "name": "safeExecuteOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "_bidLimit", "type": "uint256" }], + "name": "setBidLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_extensionTime", "type": "uint256" }, + { "internalType": "uint256", "name": "_extensionTimeLimit", "type": "uint256" } + ], + "name": "setExtensionTimeAndLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "_publicationFee", "type": "uint256" }], + "name": "setPublicationFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "_teamAddress", "type": "address" }], + "name": "setTeamAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "_tradeFee", "type": "uint256" }], + "name": "setTradeFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "teamAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tradeFee", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/config/constants/contracts.ts b/src/config/constants/contracts.ts index 1a34935..99396d2 100644 --- a/src/config/constants/contracts.ts +++ b/src/config/constants/contracts.ts @@ -49,6 +49,11 @@ export default { 56: '0x2f562A9fE0325501A6Aa92cd9e2081B026fC35aa', // NEED CHANGE IDO兑换 5: '0x81cdaf31147b86ffe6303b1bde6f20a5377d87b1', }, + hccMarketplace: { + 97: '0x91b9a998eedbb513f86b7d2a8efc01624a6d9636', + 56: '0x75c6c866f9f4ba6d0cad39d29f8e54a7ec64afb8', // NEED CHANGE NFT市场 + 5: '0x8678a822dfa815da423517b315641ef7f20020d7', + }, blindBox: { 97: '0x0e226d7b83b511ce224803b1330beb4a59bfa5d6', 56: '0x0e226d7b83b511ce224803b1330beb4a59bfa5d6', // NEED CHANGE 官方市场 盲盒 diff --git a/src/config/localization/translations.json b/src/config/localization/translations.json index 29a660e..31f75a2 100644 --- a/src/config/localization/translations.json +++ b/src/config/localization/translations.json @@ -1383,7 +1383,7 @@ "Selling time": "Selling time", "Add as a trading pair": "Add as a trading pair", "reselect NFT": "reselect NFT", - "Auction countdown": "Auction countdown", + "Auction remaining time": "Auction remaining time", "present price%price%": "present price%price%", "Fixed markup (%price% premium)": "Fixed markup (%price% premium)", "owner": "owner", @@ -1412,5 +1412,20 @@ "PROPS": "PROPS", "rarity": "rarity", "success": "success", - "Exchange closed": "Exchange closed" + "Exchange closed": "Exchange closed", + "Have they": "Have they", + "Cancel the deity": "Cancel the deity", + "abortive auction": "abortive auction", + "out": "out", + "For successful": "For successful", + "Please enter the time": "Please enter the time", + "Please enter price": "Please enter price", + "hour": "hour", + "Selling rules": "Selling rules", + "1.NFT may not be traded or transferred at the time of sale": "1.NFT may not be traded or transferred at the time of sale", + "2. After the successful sale of NFT, the platform will charge the publisher 6% of the profits as a commission fee": "2. After the successful sale of NFT, the platform will charge the publisher 6% of the profits as a commission fee", + "order status": "order status", + "Asking price": "Asking price", + "Can't be less than %num%": "Can't be less than %num%", + "They are in": "They are in" } diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts index 17c2149..3c7985f 100644 --- a/src/hooks/useContract.ts +++ b/src/hooks/useContract.ts @@ -30,6 +30,7 @@ import { getIdoPurchaseContract, getBlindBoxContract, getHolderPoolContract, + getHccMarketplaceContract, } from 'utils/contractHelpers' // Imports below migrated from Exchange useContract.ts @@ -124,6 +125,10 @@ export const useHolderPool = () => { const { library } = useActiveWeb3React() return useMemo(() => getHolderPoolContract(library.getSigner()), [library]) } +export const useHccMarketplace = () => { + const { library } = useActiveWeb3React() + return useMemo(() => getHccMarketplaceContract(library.getSigner()), [library]) +} export const useReferralRewardchef = () => { const { library } = useActiveWeb3React() return useMemo(() => getReferralRewardchefContract(library.getSigner()), [library]) diff --git a/src/services/bazaar.ts b/src/services/bazaar.ts index d4308d3..b7bf580 100644 --- a/src/services/bazaar.ts +++ b/src/services/bazaar.ts @@ -36,4 +36,61 @@ export const getNftDetail = (id, params) => { params, }) } +export const getTradePage = (params) => { + return request.request({ + url: `/high_city/app/api/market/trade/page`, + method: 'get', + params, + }) +} +export const marketTradeCreateOrderTx = (params) => { + return request.request({ + url: `/high_city/app/api/market/trade/create/order/tx`, + method: 'get', + params, + }) +} +export const marketTradeRecordDetail = (id) => { + return request.request({ + url: `/high_city/app/api/market/trade/record/detail/${id}`, + method: 'get', + params: { id }, + }) +} +export const cancelOrderTx = (tx) => { + return request.request({ + url: `/high_city/app/api/market/trade/cancel/order/tx`, + method: 'get', + params: { tx }, + }) +} +export const executeOrderTx = (tx) => { + return request.request({ + url: `/high_city/app/api/market/trade/execute/order/tx`, + method: 'get', + params: { tx }, + }) +} +export const bidOrderTx = (tx) => { + return request.request({ + url: `/high_city/app/api/market/trade/bid/order/tx`, + method: 'get', + params: { tx }, + }) +} +export const tradeRecord = (params) => { + return request.request({ + url: `/high_city/app/api/market/trade/record/option/page/${params.id}`, + method: 'get', + params, + }) +} +export const tradeRecordPage = (params) => { + return request.request({ + url: `/high_city/app/api/market/trade/record/page`, + method: 'get', + params, + }) +} + export default getOfficialPage diff --git a/src/state/bazaar/index.ts b/src/state/bazaar/index.ts new file mode 100644 index 0000000..77f8dcd --- /dev/null +++ b/src/state/bazaar/index.ts @@ -0,0 +1,21 @@ +import { createSlice } from '@reduxjs/toolkit' +import BigNumber from 'bignumber.js' +import erc20ABI from 'config/abi/erc20.json' +import multicall from 'utils/multicall' +import tokens from 'config/constants/tokens' +import { getAddress, getHccMarketplaceAddress } from 'utils/addressHelpers' + +export const fetchBazaarUserAllowances = async (account: string) => { + const tokenAddresses = getAddress(tokens.hcc.address) + const hccMarketplaceAddress = getHccMarketplaceAddress() + const calls = [{ address: tokenAddresses, name: 'allowance', params: [account, hccMarketplaceAddress] }] + const rawLpAllowances = await multicall(erc20ABI, calls) + const parsedLpAllowances = rawLpAllowances.map((balance) => { + return new BigNumber(balance).toNumber() + }) + return { + hcc: parsedLpAllowances[0], + } +} + +export default fetchBazaarUserAllowances diff --git a/src/state/blindBox/index.ts b/src/state/blindBox/index.ts index 1f7f3d9..f7b0000 100644 --- a/src/state/blindBox/index.ts +++ b/src/state/blindBox/index.ts @@ -13,8 +13,8 @@ const payWayList = { export const fetchBlindBoxUserAllowances = async (account: string) => { const calls = Object.values(payWayList).map((payWayItem) => { const tokenAddresses = getAddress(payWayItem.address) - const referralChefAddress = getBlindBoxAddress() - return { address: tokenAddresses, name: 'allowance', params: [account, referralChefAddress] } + const blindBoxAddress = getBlindBoxAddress() + return { address: tokenAddresses, name: 'allowance', params: [account, blindBoxAddress] } }) const rawLpAllowances = await multicall(erc20ABI, calls) diff --git a/src/types/bazaar.ts b/src/types/bazaar.ts index 413f290..7b231ae 100644 --- a/src/types/bazaar.ts +++ b/src/types/bazaar.ts @@ -7,6 +7,12 @@ export interface ListProps { priceList?: PriceProps[] type?: string record?: boolean + token?: string + + goodsName?: string + goodsType?: string + goodsId?: string + goodsGrade?: string } export interface CoverResourceProps { path?: string diff --git a/src/utils/addressHelpers.ts b/src/utils/addressHelpers.ts index 3074de6..953a510 100644 --- a/src/utils/addressHelpers.ts +++ b/src/utils/addressHelpers.ts @@ -77,6 +77,9 @@ export const getBunnySpecialPredictionAddress = () => { export const getIdoPurchaseAddress = () => { return getAddress(addresses.idoPurchase) } +export const getHccMarketplaceAddress = () => { + return getAddress(addresses.hccMarketplace) +} export const getBlindBoxAddress = () => { return getAddress(addresses.blindBox) } diff --git a/src/utils/contractHelpers.ts b/src/utils/contractHelpers.ts index ee6dd15..5603e8b 100644 --- a/src/utils/contractHelpers.ts +++ b/src/utils/contractHelpers.ts @@ -28,6 +28,7 @@ import { getReferralAddress, getReferralRewardAddress, getIdoPurchaseAddress, + getHccMarketplaceAddress, getBlindBoxAddress, getHolderPoolAddress, } from 'utils/addressHelpers' @@ -63,6 +64,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 hccMarketplace from 'config/abi/hccMarketplace.json' import holderPool from 'config/abi/holderPool.json' import blindBox from 'config/abi/blindBox.json' import { ChainLinkOracleContract, PredictionsContract } from './types' @@ -136,6 +138,9 @@ export const getReferralRewardchefContract = (signer?: ethers.Signer | ethers.pr export const getIdoPurchaseContract = (signer?: ethers.Signer | ethers.providers.Provider) => { return getContract(idoPurchase, getIdoPurchaseAddress(), signer) } +export const getHccMarketplaceContract = (signer?: ethers.Signer | ethers.providers.Provider) => { + return getContract(hccMarketplace, getHccMarketplaceAddress(), signer) +} export const getHolderPoolContract = (signer?: ethers.Signer | ethers.providers.Provider) => { return getContract(holderPool, getHolderPoolAddress(), signer) } diff --git a/src/views/Bazaar/components/AssetsInfo.tsx b/src/views/Bazaar/components/AssetsInfo.tsx index 021d837..d8ea252 100644 --- a/src/views/Bazaar/components/AssetsInfo.tsx +++ b/src/views/Bazaar/components/AssetsInfo.tsx @@ -7,6 +7,8 @@ import FlexCom from './FlexCom' interface DetailProps { typeIndex: number + tokenId?: string + address?: string } const DetailFlex = styled(Flex)` @@ -20,7 +22,7 @@ const DetailFlex = styled(Flex)` margin-top: 24px; ` -const AssetsInfo: React.FC = ({ typeIndex }) => { +const AssetsInfo: React.FC = ({ typeIndex, tokenId, address }) => { const { t } = useTranslation() const [link, setLink] = useState('') @@ -34,13 +36,13 @@ const AssetsInfo: React.FC = ({ typeIndex }) => { return ( - {typeIndex !== 0 && } + {typeIndex !== 0 && } - {typeIndex !== 0 && } + {typeIndex !== 0 && } diff --git a/src/views/Bazaar/components/AuctionRecord.tsx b/src/views/Bazaar/components/AuctionRecord.tsx index 335953e..c1c2874 100644 --- a/src/views/Bazaar/components/AuctionRecord.tsx +++ b/src/views/Bazaar/components/AuctionRecord.tsx @@ -1,10 +1,24 @@ -import React, { useState } from 'react' +import React, { useState, useEffect } from 'react' 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 { tradeRecordPage } from 'services/bazaar' +import Empty from 'components/Empty' interface AuctionRecordProps { onDismiss?: () => void + recordDetail?: (v) => void +} +interface ListProps { + goodsName?: string + id?: string + lastPrice?: string + price?: string + status?: string + token?: string + tx?: string + updatedAt?: string } const FlexMain = styled.div` @@ -70,7 +84,10 @@ const ThemedItem = styled.div` color: #666666; border-top: 1px solid #e3e3e3; ` -const TableBody = styled.div`` +const TableBody = styled.div` + height: 350px; + overflow-y: auto; +` const TrFlex = styled(Flex)` /* height: 80px; */ padding: 10px 0; @@ -113,15 +130,18 @@ const HashText = styled(Text)` border-bottom: 1px solid #1fc7d4; ` -const AuctionRecord: React.FC = ({ onDismiss }) => { +const AuctionRecord: React.FC = ({ onDismiss, recordDetail }) => { const { t } = useTranslation() + const [count, setCount] = useState(undefined) const typeList = [ - { label: t('All transactions'), value: '1' }, - { label: t('I released'), value: '2' }, - { label: t('I participate in'), value: '3' }, + { label: t('All transactions'), value: '' }, + { label: t('I released'), value: 'PUBLISH' }, + { label: t('I participate in'), value: 'PARTICIPATE' }, ] const [typeIndex, setTypeIndex] = useState(0) + const [pageNum, setPage] = useState(1) + const [list, setList] = useState() const ThemedList = [ t('NFT name'), @@ -131,18 +151,48 @@ const AuctionRecord: React.FC = ({ onDismiss }) => { t('state'), t('operation'), ] + const statusTxt = { + PENDING: t('They are in'), + CANCEL: t('Canceled'), + STREAMING: t('abortive auction'), + SUCCESS: t('success'), + end: t('finished'), + } - const list = [ - { - name: 'Cat goddess Emerald ', - icon: '', - price: '1', - newPrice: '2', - time: '2022-02-02', - status: '已售卖', - id: '1', - }, - ] + const getList = async () => { + setList([]) + const res = await tradeRecordPage({ + page: pageNum, + size: 4, + type: 'AUCTION', + relationType: typeList[typeIndex].value, + }) + setCount(getTotalPageNum(res.total, 4)) + setList([...res.content]) + } + + const getTotalPageNum = (total, pageSize) => { + const countTotal = ((Number(total) + Number(pageSize) - 1) / Number(pageSize)).toString() + return parseInt(countTotal) + } + const pageChange = (event, page) => { + setPage(page) + } + useEffect(() => { + getList() + }, [pageNum, typeIndex]) + const goHash = (val) => { + window.open(`https://goerli.etherscan.io/tx/${val.tx}`) + } + + const changeType = async (index) => { + setTypeIndex(index) + setPage(1) + } + const lookDetail = async (item) => { + recordDetail(item) + onDismiss() + } return ( @@ -154,7 +204,7 @@ const AuctionRecord: React.FC = ({ onDismiss }) => { setTypeIndex(index)} + onClick={() => changeType(index)} > {item.label} @@ -168,24 +218,32 @@ const AuctionRecord: React.FC = ({ onDismiss }) => { })} - {list.map((item) => { - return ( - - - - {item.name} - - {item.price} - {item.newPrice} - {item.time} - {item.status} - - {t('Detail')} - {t('deal Hash')} - - - ) - })} + {list?.length > 0 && + list.map((item) => { + return ( + + + {/* */} + {item.goodsName} + + {item.price} + {item.lastPrice} + {item.updatedAt} + {statusTxt[item.status]} + + lookDetail(item)}>{t('Detail')} + goHash(item)}>{t('deal Hash')} + + + ) + })} + {list?.length === 0 ? ( + + ) : ( + + + + )} diff --git a/src/views/Bazaar/components/AuctionRule.tsx b/src/views/Bazaar/components/AuctionRule.tsx index 36b4a72..a0c2de5 100644 --- a/src/views/Bazaar/components/AuctionRule.tsx +++ b/src/views/Bazaar/components/AuctionRule.tsx @@ -3,6 +3,10 @@ import styled from 'styled-components' import { Text, Button, Image, Flex } from '@pancakeswap/uikit' import { useTranslation } from 'contexts/Localization' +interface AuctionRuleProps { + typeIndex?: number +} + const FlexMain = styled.div` margin-top: 30px; padding: 34px 30px; @@ -22,29 +26,44 @@ const DetailText = styled(Text)` margin-bottom: 6px; ` -const AuctionRule: React.FC = () => { +const AuctionRule: React.FC = ({ typeIndex }) => { const { t } = useTranslation() return ( - {t('Auction rule')} - - {t('1. When the countdown is less than 1 hour, the countdown time will be increased by 1 hour each time')} - - - {t( - '2. Each auction has a fixed 10% markup. After the countdown, the item will be awarded to the bidder who made the last bid', - )} - - {t('3. After the auction is successful')} - - {t( - '4. After the auction, the publisher or the last bidder will collect revenue or NFT in the auction history. After one party of the bidder or the publisher receives revenue or NFT, the other party will automatically receive the corresponding revenue or NFT', - )} - - - {t('5. NFT shall not be traded or transferred after the auction is issued by the issuer')} - + {typeIndex === 1 && ( + <> + {t('Selling rules')} + {t('1.NFT may not be traded or transferred at the time of sale')} + + {t( + '2. After the successful sale of NFT, the platform will charge the publisher 6% of the profits as a commission fee', + )} + + + )} + {typeIndex === 2 && ( + <> + {t('Auction rule')} + + {t('1. When the countdown is less than 1 hour, the countdown time will be increased by 1 hour each time')} + + + {t( + '2. Each auction has a fixed 10% markup. After the countdown, the item will be awarded to the bidder who made the last bid', + )} + + {t('3. After the auction is successful')} + + {t( + '4. After the auction, the publisher or the last bidder will collect revenue or NFT in the auction history. After one party of the bidder or the publisher receives revenue or NFT, the other party will automatically receive the corresponding revenue or NFT', + )} + + + {t('5. NFT shall not be traded or transferred after the auction is issued by the issuer')} + + + )} ) } diff --git a/src/views/Bazaar/components/AuctionTable.tsx b/src/views/Bazaar/components/AuctionTable.tsx index 6ade69d..f156324 100644 --- a/src/views/Bazaar/components/AuctionTable.tsx +++ b/src/views/Bazaar/components/AuctionTable.tsx @@ -1,11 +1,39 @@ -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import styled from 'styled-components' +import Tooltip from '@mui/material/Tooltip' import { Text, Flex } from '@pancakeswap/uikit' import { useTranslation } from 'contexts/Localization' +import { tradeRecord } from 'services/bazaar' +import Empty from 'components/Empty' + +interface AuctionTableProps { + token?: string + time?: number +} +interface ListProps { + address?: string + coverResource?: CoverResource + goodsGrade?: string + goodsId?: number + goodsName?: string + goodsType?: string + id?: number + lastPrice?: number + price?: number + status?: string + token?: number + tx?: string + updatedAt?: number +} +interface CoverResource { + path?: string + url?: string +} const FlexMain = styled.div` margin-top: 30px; - height: 405px; + /* height: 405px; */ + padding-bottom: 20px; background: #fff; font-size: 18px; color: #999999; @@ -45,33 +73,51 @@ const TrFlex = styled(Flex)` align-items: center; /* flex-wrap: wrap; */ ` -const TdFlex = styled(Flex)` - align-items: center; - justify-content: center; - width: 100%; +const TdFlex = styled.div` + width: 60%; + padding: 0 20px; + text-align: center; color: #666666; font-size: 16px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; + cursor: pointer; +` +const TxFlex = styled(Flex)` + justify-content: center; + cursor: pointer; + & > .linkText:hover { + color: #1fc7d4 !important; + border-bottom: 1px solid #1fc7d4 !important; + } ` -const AuctionTable: React.FC = () => { +const AuctionTable: React.FC = ({ token, time }) => { const { t } = useTranslation() + const [pageNum, setPage] = useState(1) + const [list, serList] = useState() + // 交易记录 + const getTradeRecord = async () => { + const res = await tradeRecord({ page: pageNum, size: 10000, id: token }) + serList(res.content) + } + useEffect(() => { + if (token) { + getTradeRecord() + } + }, [pageNum, token]) + const goHash = (val) => { + window.open(`https://goerli.etherscan.io/tx/${val}`) + } const ThemedList = [t('The wallet address'), 'HASH', t('auction price'), t('The auction time'), t('state')] - - const list = [ - { - name: 'Cat goddess Emerald ', - icon: '', - price: '1', - newPrice: '2', - time: '2022-02-02', - status: '已售卖', - id: '1', - }, - ] + const statusTxt = { + PENDING: t('Cancel the deity'), + CANCEL: t('Canceled'), + STREAMING: t('abortive auction'), + SUCCESS: t('success'), + } return ( @@ -83,17 +129,41 @@ const AuctionTable: React.FC = () => { })} - {list.map((item) => { - return ( - - {item.name} - {item.price} - {item.newPrice} - {item.time} - {item.status} - - ) - })} + {list?.length > 0 ? ( + list?.map((item, index) => { + return ( + + + +
+ {item.address && item.address.substring(0, 6)}.... + {item.address && item.address.substring(item.address.length - 6, item.tx.length)} +
+
+
+ + goHash(item.tx)}> +
+ {item.tx && item.tx.substring(0, 6)}.... + {item.tx && item.tx.substring(item.tx.length - 6, item.tx.length)} +
+
+
+ {item.price} + {item.updatedAt} + + {time < new Date().getTime() && index === 0 + ? t('traded') + : time > new Date().getTime() && index === 0 + ? '-' + : t('out')} + +
+ ) + }) + ) : ( + + )}
diff --git a/src/views/Bazaar/components/Content.tsx b/src/views/Bazaar/components/Content.tsx index ec94dbd..da214e4 100644 --- a/src/views/Bazaar/components/Content.tsx +++ b/src/views/Bazaar/components/Content.tsx @@ -11,11 +11,12 @@ import useToast from 'hooks/useToast' import { ListProps } from 'types/bazaar' import Empty from 'components/Empty' -import { useGetOfficialPage } from '../hooks' +import { useGetOfficialPage, useGetTradePage } from '../hooks' import HeaderOperation from './HeaderOperation' import ContentShop from './ContentShop' import Transaction from './Transaction' import ShopDetail from './ShopDetail' +import ShopDetailBazaar from './ShopDetailBazaar' const HeaderFlex = styled(Flex)` width: 100%; @@ -138,6 +139,7 @@ const Content: React.FC = () => { const { toastSuccess, toastError } = useToast() const [detail, setDetail] = useState({}) const [detailVisible, setDetailVisible] = useState(false) + const [ids, setId] = useState('') const typeList = [ { label: t('official market'), type: 1 }, { label: t('bazaar'), type: 2 }, @@ -145,10 +147,10 @@ const Content: React.FC = () => { ] const [typeIndex, setTypeIndex] = useState(0) const auctionList = [ - { label: t('In the auction'), value: '0' }, - { label: t('traded'), value: '1' }, + { label: t('In the auction'), value: 'PENDING' }, + { label: t('traded'), value: 'SUCCESS' }, ] - const [auctionSelect, setAuctionSelect] = useState({ label: t('In the auction'), value: '0' }) + const [auctionSelect, setAuctionSelect] = useState({ label: t('In the auction'), value: 'PENDING' }) const newPrice = [ // { label: t('The latest offer'), value: '0' }, // { label: t('latest release'), value: '1' }, @@ -175,11 +177,35 @@ const Content: React.FC = () => { const [count, setCount] = useState(undefined) const getOfficialPage = useGetOfficialPage() + const getTradePage = useGetTradePage() - const getData = async () => { + const changePage = (index) => { + setGrade('') + setSearchTitle('') + setPage(1) + setTypeIndex(index) + } + // 获取市场列表 + const getTradeList = async (type) => { + setList([]) const params = { page: pageNum, - size: 10, + size: 8, + name: searchTitle, + grade: searchGrade, + status: auctionSelect.value, + type, + } + const data = await getTradePage(params) + setList(data.content) + setCount(getTotalPageNum(data.total, data.size)) + } + + const getData = async () => { + setList([]) + const params = { + page: pageNum, + size: 8, name: searchTitle, grade: searchGrade, } @@ -197,19 +223,42 @@ const Content: React.FC = () => { 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) - } - }) + if (!locationData?.type) { + arr.forEach((item) => { + if (item.id === locationData.id) { + setDetail(item) + setDetailVisible(true) + } + }) + } } setCount(getTotalPageNum(data.total, data.size)) } - useEffect(() => { - getData() - }, [pageNum, searchGrade]) + if (location.search) { + const locationData = qs.parse(location.search.slice(1)) + if (locationData.type) { + setTypeIndex(Number(locationData.type)) + setId(locationData.id.toString()) + setDetailVisible(true) + } + } + }, []) + useEffect(() => { + switch (typeIndex) { + case 0: + getData() + break + case 1: + getTradeList('NORMAL') + break + case 2: + getTradeList('AUCTION') + break + default: + getData() + } + }, [pageNum, searchGrade, typeIndex, detailVisible, auctionSelect]) useEffect(() => { setPriceSelect({ label: t('Prices go from low to high'), value: '3' }) @@ -221,9 +270,12 @@ const Content: React.FC = () => { } const searchList = () => { setPage(1) - getData() + // getData() } const showDetail = (val) => { + if (typeIndex !== 0) { + setId(val.id) + } setDetail(val) setDetailVisible(!detailVisible) } @@ -240,14 +292,10 @@ 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) => { + if (typeIndex !== 0) { + setId(val.id) + } setDetail(val) setDetailVisible(true) // detail @@ -260,7 +308,8 @@ const Content: React.FC = () => { return ( <> - {detailVisible && } + {detailVisible && typeIndex === 0 && } + {detailVisible && typeIndex !== 0 && } {!detailVisible && ( <> @@ -297,7 +346,7 @@ const Content: React.FC = () => { })} - {typeIndex !== 0 && ( + {typeIndex === 2 && ( { )} - {typeIndex !== 0 && ( + {/* {typeIndex !== 0 && ( { })} - )} + )} */} @@ -349,7 +398,7 @@ const Content: React.FC = () => { - showDetail(v)} /> + showDetail(v)} /> {list.length > 0 && ( diff --git a/src/views/Bazaar/components/ContentShop.tsx b/src/views/Bazaar/components/ContentShop.tsx index e723e12..226fdcc 100644 --- a/src/views/Bazaar/components/ContentShop.tsx +++ b/src/views/Bazaar/components/ContentShop.tsx @@ -10,9 +10,11 @@ import useRefresh from 'hooks/useRefresh' import { TOKEN_SYMBOL } from 'config/index' import ShopList from './ShopList' +import ShopListBazaar from './ShopListBazaar' interface ContentShop { list?: ListProps[] + typeIndex?: number getDetail?: (v) => void } @@ -66,7 +68,7 @@ const FooterValue = styled(Flex)` flex-wrap: wrap; ` -const ContentShop: React.FC = ({ list, getDetail }) => { +const ContentShop: React.FC = ({ list, getDetail, typeIndex }) => { const { t } = useTranslation() const account = useAccount() const { toastSuccess, toastError } = useToast() @@ -80,30 +82,54 @@ const ContentShop: React.FC = ({ list, getDetail }) => { {list.map((item) => { return ( showDetail(item)}> - - {item.name} - - {t('trading value')} - - {item.priceList.map((childItem, index) => { - return ( - - {/* <>{formatTimeNumber(childItem.value)} */} - <>{Number(childItem.value).toFixed(2)} - {childItem.label} - {index === 0 && item.priceList.length === 2 && -} - - ) - })} - - + {typeIndex === 0 && ( + <> + + {item.name} + + {t('trading value')} + + {item?.priceList?.map((childItem, index) => { + return ( + + {/* <>{formatTimeNumber(childItem.value)} */} + <>{Number(childItem.value).toFixed(2)} + {childItem.label} + {index === 0 && item.priceList.length === 2 && -} + + ) + })} + + + + )} + {typeIndex !== 0 && ( + <> + + {item?.goodsName} + + {t('trading value')} + + {item?.price} + {TOKEN_SYMBOL} + + + + )} ) })} diff --git a/src/views/Bazaar/components/HeaderOperation.tsx b/src/views/Bazaar/components/HeaderOperation.tsx index ce67c1a..a0dd209 100644 --- a/src/views/Bazaar/components/HeaderOperation.tsx +++ b/src/views/Bazaar/components/HeaderOperation.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'contexts/Localization' import { Flex, Button, Text, Input, Image, useModal, Dropdown } from '@pancakeswap/uikit' import { useAccount } from 'state/userInfo/hooks' import useRefresh from 'hooks/useRefresh' +import UnlockButton from 'components/UnlockButton' import TransactionRecord from './TransactionRecord' import AuctionRecord from './AuctionRecord' @@ -34,24 +35,35 @@ const HeaderButton = styled(Button)` margin-right: 0px; } ` +const UnlockButtonDiv = styled(UnlockButton)` + width: 110px; + height: 40px; + border: 1px solid #1fc7d4; + border-radius: 30px; + font-size: 14px; + color: #1fc7d4; + background-color: #fff; +` const HeaderOperation: React.FC = ({ activeIndex, getDetail }) => { const { t } = useTranslation() + const account = useAccount() const [onTransactionRecord] = useModal( getDetail(v)} />, ) - const [onAuctionRecord] = useModal() + const [onAuctionRecord] = useModal( getDetail(v)} />) const [onSellModal] = useModal() return ( - {t('I want to sell')} + {!account && } + {account && {t('I want to sell')}} {/* 当顶部切换选中的是全部和市场则显示交易记录,选中拍卖时展示拍卖纪录 */} - {activeIndex === 2 ? ( + {activeIndex === 2 && account ? ( {t('Auctions a record')} ) : ( - {t('transaction record')} + account && {t('transaction record')} )} ) diff --git a/src/views/Bazaar/components/SellInput.tsx b/src/views/Bazaar/components/SellInput.tsx new file mode 100644 index 0000000..5190298 --- /dev/null +++ b/src/views/Bazaar/components/SellInput.tsx @@ -0,0 +1,54 @@ +import React, { useState } from 'react' +import styled from 'styled-components' +import { Text, Button, Image, Input, Flex, Dropdown } from '@pancakeswap/uikit' +import { useTranslation } from 'contexts/Localization' +import ShopList from './ShopList' +import ShopModal from './ShopModal' + +interface sellModalProps { + onDismiss?: () => void + getInputValue?: (v) => void +} +interface Detail { + address?: string + createdAt?: string + id?: string + info: InfoProps + token?: string + updatedAt?: string + select?: boolean +} +interface InfoProps { + coverResource?: CoverResourceProps + grade?: string + id?: string + name?: string + price?: any + type?: string +} +interface CoverResourceProps { + path?: string + url?: string +} + +const PriceInput = styled(Input)` + width: 180px; + height: 35px; + margin-top: 20px; +` + +const SellInput: React.FC = ({ onDismiss, getInputValue }) => { + const { t } = useTranslation() + const [inputValue, setValue] = useState('') + + const handleChange = (evt: React.ChangeEvent) => { + const { value } = evt.target + setValue(value) + getInputValue(value) + } + return ( + + ) +} + +export default SellInput diff --git a/src/views/Bazaar/components/SellModal.tsx b/src/views/Bazaar/components/SellModal.tsx index d9d8c3f..1b3cc36 100644 --- a/src/views/Bazaar/components/SellModal.tsx +++ b/src/views/Bazaar/components/SellModal.tsx @@ -1,12 +1,42 @@ -import React, { useState } from 'react' +import React, { useState, useEffect } from 'react' +import { useLocation, useHistory, useParams } from 'react-router-dom' import styled from 'styled-components' import { Text, Button, Image, Input, Flex, Dropdown } from '@pancakeswap/uikit' import { useTranslation } from 'contexts/Localization' +import useToast from 'hooks/useToast' +import { getHccMarketplaceAddress } from 'utils/addressHelpers' +import useRefresh from 'hooks/useRefresh' +import { marketTradeCreateOrderTx } from 'services/bazaar' +import BigNumber from 'bignumber.js' +import { useCreateNormalOrder, useCreateAuctionOrder, useIsApproved, useApproved } from '../hooks' import ShopList from './ShopList' import ShopModal from './ShopModal' interface sellModalProps { onDismiss?: () => void + detailData?: Detail +} +interface Detail { + address?: string + createdAt?: string + id?: string + info: InfoProps + token?: string + updatedAt?: string + select?: boolean + pending?: boolean +} +interface InfoProps { + coverResource?: CoverResourceProps + grade?: string + id?: string + name?: string + price?: any + type?: string +} +interface CoverResourceProps { + path?: string + url?: string } const FlexMain = styled.div` @@ -70,8 +100,10 @@ const SellingWayFlex = styled(Flex)` } ` const WayFlex = styled(Flex)` - width: 400px; - flex-direction: column; + width: 100%; + justify-content: space-between; + flex-wrap: wrap; + /* flex-direction: column; */ ` const FlexOption = styled(Flex)` align-items: center; @@ -88,7 +120,6 @@ const FlexOption = styled(Flex)` } ` const SellInput = styled(Input)` - margin-top: 20px; width: 400px; height: 35px; background: #eeeaf4; @@ -102,78 +133,6 @@ const SellInput = styled(Input)` width: 400px; } ` -const FlexPrice = styled(Flex)` - width: 500px; - flex-wrap: wrap; - align-items: center; - - ${({ theme }) => theme.mediaQueries.xs} { - width: 100%; - justify-content: center; - margin-top: 10px; - } - ${({ theme }) => theme.mediaQueries.lg} { - width: 500px; - justify-content: flex-start; - } -` -const TransactionMain = styled(Flex)` - align-items: center; - ${({ theme }) => theme.mediaQueries.xs} { - margin-top: 10px; - } - ${({ theme }) => theme.mediaQueries.lg} { - margin-top: 0px; - } -` -const PriceItem = styled(Flex)` - width: 180px; - /* margin-right: 80px; */ - flex-direction: column; -` -const FlexCoinOption = styled(Flex)` - align-items: center; - justify-content: center; - width: 180px; - height: 40px; - cursor: pointer; -` -const PriceWay = styled(Flex)` - align-items: center; - justify-content: space-between; - width: 180px; - height: 35px; - background: #eeeaf4; - border: 1px solid #d7caec; - border-radius: 18px; - padding: 0 15px; - color: #666666; - font-size: 16px; -` -const PriceInput = styled(Input)` - width: 180px; - height: 35px; - margin-top: 20px; -` -const AddButton = styled(Button)` - width: 140px; - height: 35px; - border: 1px solid #1fc7d4; - border-radius: 30px; - font-size: 14px; - color: #1fc7d4; - background-color: #fff; - margin-left: 80px; - - ${({ theme }) => theme.mediaQueries.xs} { - margin-left: 0px; - margin-top: 10px; - } - ${({ theme }) => theme.mediaQueries.lg} { - margin-left: 80px; - margin-top: 0px; - } -` const Shop = styled(Flex)` /* min-height: 366px; */ padding: 30px 0; @@ -189,12 +148,6 @@ const AddNftButton = styled(Button)` color: #fff; font-size: 14px; ` -const PriceImage = styled(Image)` - margin: 0 10px; - width: 20px; - height: 20px; - cursor: pointer; -` const ShopFlex = styled(Flex)` width: 278px; flex-direction: column; @@ -234,44 +187,121 @@ const SelectShopModal = styled(Flex)` z-index: 999; /* background-color: #7a6eaa; */ ` +const InputDiv = styled.div` + position: relative; +` +const InputText = styled(Text)` + position: absolute; + right: 10px; + top: 4px; +` -const SellModal: React.FC = ({ onDismiss }) => { +const SellModal: React.FC = ({ onDismiss, detailData }) => { const { t } = useTranslation() - const [tradingOnList, setTradingOnList] = useState([{ price: '', value: 'HCC', id: 1 }]) - const [sellingWay, setSellingWay] = useState(undefined) - const [coin, setCoin] = useState({ label: 'HCC', value: 'HCC' }) - const [shopData, setShopData] = useState({ label: 'Cat goddess Emerald ', type: 1, id: 1 }) + const history = useHistory() + const { fastRefresh } = useRefresh() + const [isApprovedFor, setIsApprovedFor] = useState(false) + const [token, setTokenId] = useState('') + const { toastSuccess, toastError } = useToast() + const [price, setPrice] = useState('') + const [sellingWay, setSellingWay] = useState({ label: t('fixed price'), value: '1' }) + const [time, setTime] = useState('') + const [shopData, setShopData] = useState() const [showModal, setShowModal] = useState(false) - - const coinList = [ - { label: 'HCC', value: 'HCC' }, - { label: 'USDT', value: 'USDT' }, - ] + const [txId, setTxId] = useState() + const [loading, setLoading] = useState(false) + const [createOrderId, setCreateOrderId] = useState('') + const [agreementLoading, setAgreementLoading] = useState(false) const SellingWayList = [ - { label: t('Auction'), value: '0' }, { label: t('fixed price'), value: '1' }, + { label: t('Auction'), value: '2' }, ] - const selectTradingOn = (index, value) => { - tradingOnList[index].value = value - setTradingOnList([...tradingOnList]) - } - const addTradingOn = () => { - tradingOnList.push({ price: '', value: 'HCC', id: 2 }) - setTradingOnList([...tradingOnList]) - } - const reduceTradingOn = () => { - tradingOnList.splice(1, 1) - setTradingOnList([...tradingOnList]) - } - const onSelectNft = () => { setShowModal(!showModal) } const close = () => { setShowModal(false) } + const getShopDetail = (v) => { + setShopData(v) + getIsApprovedForAll(v.token) + setShowModal(false) + } + const handlePriceChange = (evt: React.ChangeEvent) => { + const { value } = evt.target + setPrice(value) + } + const handleChange = (evt: React.ChangeEvent) => { + const { value } = evt.target + setTime(value) + } + const createAuctionOrder = useCreateAuctionOrder() + const createNormalOrder = useCreateNormalOrder() + const isApproved = useIsApproved() + const approved = useApproved() + const getIsApprovedForAll = async (tokenId) => { + const res = await isApproved(tokenId) + setIsApprovedFor(res) + if (res) { + setAgreementLoading(false) + } + } + + const sellNow = async () => { + if (sellingWay?.value === '2' && !time) { + toastError(t('Please enter the time')) + return + } + // const priceVisible = tradingOnList.find((item) => !item.price) + if (!price) { + toastError(t('Please enter price')) + return + } + if (Number(price) < 0) { + toastError(t("Can't be less than %num%", { num: 0 })) + return + } + if (sellingWay?.value === '2' && Number(time) < 0) { + toastError(t("Can't be less than %num%", { num: 0 })) + return + } + setLoading(true) + let res = { hash: undefined } + if (sellingWay?.value === '2') { + const auctionTime = new BigNumber(time).multipliedBy(3600000).plus(new Date().getTime()).toNumber() + // const auctionTime = Number(time) * 3600000 + new Date().getTime() + res = await createAuctionOrder(shopData.token, price, auctionTime) + } else { + res = await createNormalOrder(shopData.token, price) + } + + setTxId(res.hash) + } + const agreement = async () => { + setAgreementLoading(true) + const res = await approved(shopData.token) + } + const getMarketTradeCreateOrderTx = async () => { + const res = await marketTradeCreateOrderTx({ tx: txId }) + if (res) { + setLoading(false) + setCreateOrderId(res) + window.location.href = `${window.location.href}?id=${res}&type=${sellingWay?.value}` + } + } + useEffect(() => { + detailData && setShopData(detailData) + }, []) + useEffect(() => { + if (!isApprovedFor) { + shopData && getIsApprovedForAll(shopData.token) + } + if (txId && loading) { + getMarketTradeCreateOrderTx() + } + }, [fastRefresh]) return ( @@ -282,7 +312,7 @@ const SellModal: React.FC = ({ onDismiss }) => { + {sellingWay ? sellingWay.label : t('Selling way')} @@ -296,58 +326,53 @@ const SellModal: React.FC = ({ onDismiss }) => { ) })} - {sellingWay?.value === '0' && } + + + HCC + + {sellingWay?.value === '2' && ( + + + {t('hour')} + + )} - - <> - {tradingOnList.map((item, index) => { - return ( - - - - {item.value} - - - } - > - {coinList.map((coinItem) => { - return ( - selectTradingOn(index, coinItem.value)}> - {coinItem.label} - - ) - })} - - - - {index === 0 && tradingOnList.length === 2 && ( - - )} - {index === 1 && ( - - )} - - ) - })} - - {tradingOnList.length < 2 && {t('Add as a trading pair')}} - - {/* {t('add NFT)} */} - - - {shopData.label} - - {t('Selling immediately')} - {t('reselect NFT')} + {/* isApprovedFor */} + {!shopData?.info?.id && {t('add NFT')}} + {shopData?.info?.id && ( + + + {shopData?.info?.name} + + )} + {shopData?.info?.id && isApprovedFor && ( + + {t('Selling immediately')} + + )} + {shopData?.info?.id && {t('reselect NFT')}} + {shopData?.info?.id && !isApprovedFor && ( + + {t('license agreement')} + + )} {showModal && ( - + getShopDetail(v)} /> )} diff --git a/src/views/Bazaar/components/ShopDetail.tsx b/src/views/Bazaar/components/ShopDetail.tsx index 2ecec3d..abab6b8 100644 --- a/src/views/Bazaar/components/ShopDetail.tsx +++ b/src/views/Bazaar/components/ShopDetail.tsx @@ -194,22 +194,32 @@ const ShopDetail: React.FC = ({ close, detail, typeIndex }) => { {typeIndex !== 0 && ( )} - - {detail.record ? ( - '' + {!detail.record && ( + + )} + {detail.record && ( + + )} + {!detail.record && typeIndex === 0 && !account && } + {!detail.record && typeIndex === 0 && account && } + {!detail.record && typeIndex !== 0 && account && ( + {t('Fixed markup (%price% premium)', { price: '10%' })} + )} + {/* {detail.record ? ( + ) : typeIndex === 0 ? ( !account ? ( @@ -218,7 +228,7 @@ const ShopDetail: React.FC = ({ close, detail, typeIndex }) => { ) ) : ( {t('Fixed markup (%price% premium)', { price: '10%' })} - )} + )} */} diff --git a/src/views/Bazaar/components/ShopDetailBazaar.tsx b/src/views/Bazaar/components/ShopDetailBazaar.tsx new file mode 100644 index 0000000..16040e9 --- /dev/null +++ b/src/views/Bazaar/components/ShopDetailBazaar.tsx @@ -0,0 +1,547 @@ +import React, { useState, useEffect } from 'react' +import dayjs from 'dayjs' +import styled from 'styled-components' +import { useTranslation } from 'contexts/Localization' +import { Flex, Button, Text, Image, useModal } 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 { + marketTradeRecordDetail, + checkBuyResult, + cancelOrderTx, + executeOrderTx, + bidOrderTx, + tradeRecord, +} from 'services/bazaar' +import { fetchBazaarUserAllowances } from 'state/bazaar' +import UnlockButton from 'components/UnlockButton' +import { getAddress } from 'utils/addressHelpers' +import { useERC20 } from 'hooks/useContract' +import tokens from 'config/constants/tokens' +import BigNumber from 'bignumber.js' +import { + useBuyTransaction, + useMarketplaceApproveHcc, + useCancelOrder, + useExecuteOrder, + useOrderMap, + useBidAuctionOrder, +} from '../hooks' +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' +import SellModal from './SellModal' + +interface DetailProps { + close: () => void + id: string + typeIndex: number +} + +interface ListProps { + address?: string + coverResource?: CoverResourceProps + goodsGrade?: string + goodsId?: string + goodsName?: string + goodsType?: string + id?: string + lastPrice?: string + optionList?: OptionListProps[] + price?: string + status?: string + token?: string + tx?: string + updatedAt?: string +} +interface CoverResourceProps { + path?: string + url?: string +} +interface OptionListProps { + address?: string + id?: string + price?: string + tx?: string +} + +const HeaderFlex = styled(Flex)` + width: 100%; + align-items: center; +` +const FirstText = styled(Text)` + font-size: 16px; + color: #1fc7d4; + cursor: pointer; +` +const ShopText = styled(Text)` + color: #666666; + font-size: 16px; + cursor: pointer; +` +const MainFlex = styled(Flex)` + margin-top: 36px; + padding: 30px; + background: #fff; + box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.16); + border-radius: 20px; + align-items: center; + justify-content: space-between; + ${({ theme }) => theme.mediaQueries.xs} { + flex-wrap: wrap; + justify-content: center; + } + ${({ theme }) => theme.mediaQueries.lg} { + flex-wrap: nowrap; + justify-content: space-between; + } +` +const ShopFlex = styled(Flex)` + width: 476px; + flex-direction: column; + border-radius: 20px; + position: relative; +` +const Detail = styled(Flex)` + width: 614px; + flex-direction: column; + justify-content: space-between; + /* height: 590px; */ + box-sizing: border-box; + border-radius: 20px; + margin-left: 30px; + ${({ theme }) => theme.mediaQueries.xs} { + margin-top: 30px; + margin-left: 0px; + width: 482px; + } + ${({ theme }) => theme.mediaQueries.lg} { + margin-top: 0px; + margin-left: 30px; + width: 614px; + } +` +const TitleText = styled(Text)` + font-size: 28px; + color: #333333; + text-align: center; +` +const PriceButton = styled(Button)` + width: 100%; + height: 60px; + background: linear-gradient(269deg, #1fc8d3 0%, #1fd4b0 100%); + border-radius: 30px; + font-size: 16px; + margin-top: 20px; +` +const AuthorizationBtn = styled(Button)` + width: 100%; + height: 60px; + background: linear-gradient(269deg, #1fc8d3 0%, #1fd4b0 100%); + border-radius: 30px; + font-size: 16px; + margin-top: 20px; +` + +const UnlockButtonDiv = styled(UnlockButton)` + width: 100%; + height: 60px; + background: linear-gradient(269deg, #1fc8d3 0%, #1fd4b0 100%); + border-radius: 30px; + font-size: 16px; + margin-top: 20px; +` +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, id, typeIndex }) => { + const { t } = useTranslation() + const [txId, setTxId] = useState() + const [txCancelId, setTxCancelId] = useState() + const [cancelStatus, setCancelStatus] = useState(false) + const [statusBtnTxt, setStatusBtnTxt] = useState('') + const statusTxt = { + PENDING: t('Cancel the deity'), + CANCEL: t('Canceled'), + STREAMING: t('abortive auction'), + SUCCESS: t('success'), + } + const statusHeaderTxt = { + PENDING: t('They are in'), + CANCEL: t('Canceled'), + STREAMING: t('abortive auction'), + SUCCESS: t('success'), + } + + const [loading, setLoading] = useState(false) + const { toastSuccess, toastError } = useToast() + const { fastRefresh } = useRefresh() + const [detail, setDetail] = useState() + const [countDown, setCountDown] = useState('') + const account = useAccount() + const [ownOrder, setOwnOrder] = useState(false) + const [forbid, setForbid] = useState(false) + const [cancelDeity, setCancelDeity] = useState(false) + const [approveStatus, setApproveStatus] = useState(false) + const [bidAuctionStatus, setBidAuctionStatus] = useState(true) + const [orderMapData, setOrderMapData] = useState(undefined) + const [sellData, setSellData] = useState(undefined) + const [allowance, setAllowance] = useState(0) + const executeOrder = useExecuteOrder() + const [txExecuteId, setTxExecuteId] = useState('') + const [txAuctionId, setTxAuctionId] = useState('') + const [sellVerify, setSellVerify] = useState(false) + const cancelOrderHook = useCancelOrder() + const orderMap = useOrderMap() + const bidAuctionOrder = useBidAuctionOrder() + const hccContract = useERC20(getAddress(tokens.hcc.address)) + const { onApprove: onHccApprove } = useMarketplaceApproveHcc(hccContract) + + useEffect(() => { + getDetail() + if (account) { + getAllowances() + } + }, [account]) + useEffect(() => { + if (txId && loading) { + getTransactionResult() + } + }, [fastRefresh]) + useEffect(() => { + // 取消挂单 + if (txCancelId && loading) { + cancelOrderTxFun() + } + // 购买 + if (txExecuteId && loading) { + executeOrderFn() + } + // 竞价 + if (txAuctionId && loading) { + bidOrderTxFn() + } + if (detail) { + getOrderMap(detail.token) + } + // 取消挂单按钮 + if (account && ownOrder && !cancelStatus) { + setCancelDeity(true) + } else { + setCancelDeity(false) + } + }, [fastRefresh]) + useEffect(() => { + if (typeIndex === 2) { + const date2 = dayjs(new Date(orderMapData?.expiresAt).getTime()).diff(dayjs()) + if (date2 > 0) { + setTimeout(() => { + const time = new Date(orderMapData?.expiresAt).getTime() - 1 + countDownFun(time) + }, 1000) + } else { + setCountDown(t('finished')) + // setStatusBtnTxt('end') + } + } + }, [countDown, orderMapData]) + + const getAllowances = async () => { + const allowances = await fetchBazaarUserAllowances(account) + setAllowance(allowances.hcc) + // 授权HCC按钮 + if (!allowances.hcc && !cancelDeity) { + setApproveStatus(true) + } else { + setApproveStatus(false) + } + } + const getDetail = async () => { + const res = await marketTradeRecordDetail(id) + if (res.address === account) { + setOwnOrder(true) + setCancelDeity(true) + } else { + setOwnOrder(false) + setCancelDeity(false) + } + setSellData({ + address: res?.address, + info: { + coverResource: res?.coverResource, + grade: res?.goodsGrade, + id: res?.id, + name: res?.goodsName, + price: res?.price, + type: res?.goodsType, + }, + updatedAt: res?.updatedAt, + token: res?.token, + }) + setStatusBtnTxt(res.status) + setDetail(res) + } + + const getTransactionResult = async () => { + const res = await checkBuyResult({ tx: txId }) + if (res) { + setLoading(false) + setTxId(undefined) + toastSuccess(t('purchase succeeds')) + } + } + + const handleApprove = async (approve) => { + try { + setLoading(true) + await approve() + setLoading(false) + getAllowances() + } catch (e) { + console.error(e) + } + } + + 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')) + } + + const cancelOrder = async () => { + setLoading(true) + const res = await cancelOrderHook(detail.token) + setTxCancelId(res.hash) + } + const executeOrderFn = async () => { + setLoading(true) + const res = await executeOrderTx(txExecuteId) + if (res) { + getDetail() + setTxExecuteId(undefined) + setLoading(false) + setSellVerify(true) + } + } + const bidOrderTxFn = async () => { + setLoading(true) + const res = await bidOrderTx(txAuctionId) + if (res) { + getDetail() + setTxExecuteId(undefined) + setLoading(false) + toastSuccess(t('For successful')) + } + } + const cancelOrderTxFun = async () => { + const res = await cancelOrderTx(txCancelId) + if (res) { + getDetail() + setCancelStatus(true) + setTxCancelId(undefined) + setLoading(false) + } + } + // 购买 + const onSell = async () => { + setLoading(true) + const res = await executeOrder(detail.token, detail.price) + setTxExecuteId(res.hash) + } + const getNft = async () => { + setLoading(true) + const res = await executeOrder(detail.token, 0) + setTxExecuteId(res.hash) + } + const getOrderMap = async (token) => { + const res = await orderMap(token) + // 竞价按钮 + if (allowance && !ownOrder && typeIndex === 2 && res?.expiresAt > new Date().getTime()) { + setBidAuctionStatus(true) + } else { + setBidAuctionStatus(false) + } + if (res.bidder.toLowerCase() === account.toLowerCase()) { + setForbid(true) + } else { + setForbid(false) + } + setOrderMapData(res) + } + const onBidAuctionOrder = async () => { + // bidder + let prices = 0 + if (orderMapData?.bidder === '0') { + prices = new BigNumber(orderMapData.price).multipliedBy(0.1).plus(orderMapData.price).toNumber() + // prices = Number(orderMapData.price) + new BigNumber(orderMapData.price).multipliedBy(0.1).toNumber() + } else { + prices = new BigNumber(orderMapData.bid).multipliedBy(0.1).plus(orderMapData.bid).toNumber() + // prices = Number(orderMapData.bid) + new BigNumber(orderMapData.bid).multipliedBy(0.1).toNumber() + } + setLoading(true) + const res = await bidAuctionOrder(detail.token, prices) + setTxAuctionId(res.hash) + } + 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 { + setCountDown(t('finished')) + setStatusBtnTxt('end') + } + } + + return ( + <> + + {t('Bazaar')} + > {detail?.goodsName} + + + + + + + + + {detail?.goodsName} + + + {orderMapData?.expiresAt > new Date().getTime() && typeIndex === 2 && ( + + )} + {statusBtnTxt !== 'PENDING' && ( + + )} + {typeIndex !== 2 && ( + + )} + {typeIndex === 2 && ( + + )} + {!account && } + {approveStatus && ( + { + handleApprove(onHccApprove) + }} + > + {t('Approve %coin% Contract', { coin: 'HCC' })} + + )} + {allowance && !ownOrder && sellVerify && typeIndex !== 2 ? ( + {statusTxt[statusBtnTxt]} + ) : ( + '' + )} + {/* 改版 */} + {/* 竞拍取消挂单:没有人竞拍时 */} + {account && allowance && ownOrder && orderMapData?.bidder === '0' ? ( + + {statusTxt[statusBtnTxt]} + + ) : ( + '' + )} + {allowance && !ownOrder && !sellVerify && typeIndex === 1 ? ( + + {t('Buy It Now')} + + ) : ( + '' + )} + {bidAuctionStatus && ( + + {t('Fixed markup (%price% premium)', { price: '10%' })} + + )} + {account && + orderMapData?.expiresAt < new Date().getTime() && + account === orderMapData?.bidder && + typeIndex === 2 ? ( + + {t('Claim now')} + + ) : ( + typeIndex === 2 && + account && + !ownOrder && + !approveStatus && + !bidAuctionStatus && {statusTxt[statusBtnTxt]} + )} + + + + + {typeIndex === 2 && } + {/* {typeIndex !== 0 && } */} + {typeIndex !== 0 && } + + ) +} +export default ShopDetail diff --git a/src/views/Bazaar/components/ShopListBazaar.tsx b/src/views/Bazaar/components/ShopListBazaar.tsx new file mode 100644 index 0000000..ab38654 --- /dev/null +++ b/src/views/Bazaar/components/ShopListBazaar.tsx @@ -0,0 +1,149 @@ +import React, { useState, useEffect } from 'react' +import styled from 'styled-components' +import { useTranslation } from 'contexts/Localization' +import { Flex, Image } from '@pancakeswap/uikit' + +const MainFlex = styled.div` + & > .epicBcg { + background-color: #ffd7d7; + } + & > .legendBcg { + background-color: #d7d7ff; + } + & > .uncommonBcg { + background-color: #cdf7d2; + } + & > .commonBcg { + background-color: #daf0ff; + } + + ${({ theme }) => theme.mediaQueries.xs} { + flex-direction: column; + } + ${({ theme }) => theme.mediaQueries.md} { + flex-direction: row; + } + ${({ theme }) => theme.mediaQueries.lg} { + flex-direction: row; + } +` + +const ShopItem = styled(Flex)` + background: rgba(255, 255, 255, 0.39); + box-shadow: 0px 1px 8px rgba(0, 0, 0, 0.15); + flex-direction: column; + position: relative; + overflow: hidden; + cursor: pointer; + + & > .ribbon { + width: 106px; + height: 108px; + overflow: hidden; + position: absolute; + top: -6px; + left: -6px; + z-index: 10; + & > .ribbon1 { + line-height: 14px; + text-align: center; + transform: rotate(-45deg); + position: relative; + 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: 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%); + } + & > .legend { + background: linear-gradient(-90deg, #4b84f5 0%, #bc21f3 100%); + } + & > .uncommon { + background: linear-gradient(-90deg, #3dffec 0%, #24bf52 100%); + } + & > .common { + background: linear-gradient(-90deg, #b5e9f3 0%, #1195d9 100%); + } + } +` + +interface ShopListItemProps { + item?: Detail + width?: number + height?: number + borderRadius?: string + grade?: string + img?: string +} +interface Detail { + label?: string + type?: number | string + id?: number | string +} + +const ShopListBazaar: React.FC = ({ + item, + width = 186, + height = 187, + borderRadius = '20px', + img, + grade, +}) => { + const { t } = useTranslation() + const getClassBcg = () => { + let bcg = '' + switch (grade) { + case 'EPIC': + bcg = 'epicBcg' + break + case 'LEGEND': + bcg = 'legendBcg' + break + case 'RARE': + bcg = 'uncommonBcg' + break + case 'NORMAL': + bcg = 'commonBcg' + break + default: + bcg = 'epicBcg' + } + return bcg + } + + return ( + + +
+ {grade === 'EPIC' &&
{t('epic')}
} + {grade === 'LEGEND' &&
{t('legend')}
} + {grade === 'RARE' &&
{t('uncommon')}
} + {grade === 'NORMAL' &&
{t('common')}
} +
+ {img ? ( + + ) : ( + + )} +
+
+ ) +} +export default ShopListBazaar diff --git a/src/views/Bazaar/components/ShopModal.tsx b/src/views/Bazaar/components/ShopModal.tsx index eafaaea..b25b89b 100644 --- a/src/views/Bazaar/components/ShopModal.tsx +++ b/src/views/Bazaar/components/ShopModal.tsx @@ -1,7 +1,10 @@ import React, { useState, useEffect } from 'react' +import Pagination from '@mui/material/Pagination' import styled from 'styled-components' import { useTranslation } from 'contexts/Localization' import { Flex, Text, Button } from '@pancakeswap/uikit' +import Empty from 'components/Empty' +import { useGetSelfPage } from '../../NftBox/hooks' import ShopList from './ShopList' interface ShopProp { @@ -9,8 +12,31 @@ interface ShopProp { value?: string | number onDismiss?: () => void close: () => void + add?: (v) => void } +interface Detail { + address?: string + createdAt?: string + id?: string + info: InfoProps + token?: string + updatedAt?: string + select?: boolean + pending?: boolean +} +interface InfoProps { + coverResource?: CoverResourceProps + grade?: string + id?: string + name?: string + price?: any + type?: string +} +interface CoverResourceProps { + path?: string + url?: string +} const Main = styled.div` width: 60%; background-color: #fff; @@ -63,6 +89,7 @@ const SelectFlex = styled(Flex)` width: 30px; height: 30px; background: #fff; + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.16); border-radius: 50%; z-index: 9; align-items: center; @@ -100,9 +127,35 @@ const OutButton = styled(Button)` margin-top: 20px; ` -const ShopModal: React.FC = ({ name, value, onDismiss, close }) => { +const ShopModal: React.FC = ({ name, value, onDismiss, close, add }) => { const { t } = useTranslation() - const [list, setList] = useState([]) + const [list, setList] = useState() + const [pageNum, setPage] = useState(1) + const [count, setCount] = useState(undefined) + const getSelfPage = useGetSelfPage() + const getData = async () => { + const params = { + page: pageNum, + size: 8, + pending: false, + } + const { size, total, content } = await getSelfPage(params) + const arr = [] + content.forEach((item) => { + const obj = item + obj.select = false + arr.push(item) + }) + setList([...arr]) + setCount(getTotalPageNum(total, size)) + } + const getTotalPageNum = (total, pageSize) => { + const countTotal = ((Number(total) + Number(pageSize) - 1) / Number(pageSize)).toString() + return parseInt(countTotal) + } + const pageChange = (event, page) => { + setPage(page) + } const pitchOn = (index) => { const arr = [] @@ -116,32 +169,44 @@ const ShopModal: React.FC = ({ name, value, onDismiss, close }) => { const onClose = () => { close() } + // 添加nft + const addNft = () => { + const info = list.find((item) => item.select) + add(info) + } useEffect(() => { - setList([ - { label: 'Cat goddess Emerald ', type: 1, id: 1, select: true }, - { label: 'Cat goddess Emerald ', type: 2, id: 2, select: false }, - { label: 'Cat goddess Emerald ', type: 3, id: 3, select: false }, - { label: 'Cat goddess Emerald ', type: 4, id: 4, select: false }, - { label: 'Cat goddess Emerald ', type: 1, id: 5, select: false }, - ]) - }, []) + getData() + }, [pageNum]) return (
- {list.map((item, index) => { - return ( - pitchOn(index)} className={item.select ? 'active' : ''}> - {item.select && } - - {item.label} - - ) - })} + {list?.length > 0 && + list?.map((item, index) => { + return ( + pitchOn(index)} className={item?.select ? 'active' : ''}> + {item?.select && } + + {item?.info?.name} + + ) + })} + {list?.length > 0 && ( + + + + )} + {list?.length === 0 && } - {t('add NFT')} + {list?.length > 0 && {t('add NFT')}} {t('Sign out')}
diff --git a/src/views/Bazaar/components/TransactionRecord.tsx b/src/views/Bazaar/components/TransactionRecord.tsx index a3712d2..2ae0e6f 100644 --- a/src/views/Bazaar/components/TransactionRecord.tsx +++ b/src/views/Bazaar/components/TransactionRecord.tsx @@ -5,6 +5,7 @@ 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 { tradeRecordPage } from 'services/bazaar' import { useGetPurchaseRecord, useGetNftDetail } from '../hooks' interface TransactionRecordProps { @@ -137,6 +138,37 @@ const TransactionRecord: React.FC = ({ onDismiss, active const [count, setCount] = useState(undefined) const getPurchaseRecord = useGetPurchaseRecord() + + const getWayList = async () => { + let relationType = '' + let status = '' + switch (typeIndex) { + case 0: + relationType = '' + status = '' + break + case 1: + relationType = 'PUBLISH' + status = 'PENDING' + break + case 2: + relationType = 'PUBLISH' + status = 'SUCCESS' + break + case 3: + relationType = 'PARTICIPATE' + status = 'SUCCESS' + break + default: + relationType = '' + status = '' + } + const obj = { page: pageNum, size: 10, type: 'NORMAL', relationType, status } + const res = await tradeRecordPage(obj) + setCount(getTotalPageNum(res.total, 4)) + setList(res.content) + } + const getList = async () => { const result = await getPurchaseRecord(pageNum, 10) setCount(getTotalPageNum(result.total, result.size)) @@ -154,8 +186,12 @@ const TransactionRecord: React.FC = ({ onDismiss, active } useEffect(() => { - getList() - }, [pageNum]) + if (activeIndex !== 0) { + getWayList() + } else { + getList() + } + }, [pageNum, typeIndex]) const pageChange = (event, page) => { setPage(page) @@ -169,13 +205,22 @@ const TransactionRecord: React.FC = ({ onDismiss, active } const getNftDetail = useGetNftDetail() const lookDetail = async (item) => { - const res = await getNftDetail(item.token, { token: item.token }) - const obj = res.info - obj.record = true + let obj = { record: false, token: '' } + if (activeIndex !== 0) { + obj = item + } else { + const res = await getNftDetail(item.token, { token: item.token }) + obj = res.info + obj.record = true + obj.token = res.token + } recordDetail(obj) onDismiss() } - + const changeType = (index) => { + setTypeIndex(index) + setPage(1) + } return ( @@ -187,7 +232,7 @@ const TransactionRecord: React.FC = ({ onDismiss, active setTypeIndex(index)} + onClick={() => changeType(index)} > {item.label} @@ -207,18 +252,22 @@ const TransactionRecord: React.FC = ({ onDismiss, active return ( {item.goodsName} - - {item.priceList.map((childItem, childIndex) => { - return ( - - <>{Number(childItem.value).toFixed(2)} - {childItem.label} - {childIndex === 0 && item.priceList.length === 2 && -} - - ) - })} - - {dayjs(Number(item.tradeTime)).format('YYYY-MM-DD HH:mm:ss')} + {activeIndex === 0 && ( + + {item.priceList.map((childItem, childIndex) => { + return ( + + <>{Number(childItem.value).toFixed(2)} + {childItem.label} + {childIndex === 0 && item.priceList.length === 2 && -} + + ) + })} + + )} + {activeIndex !== 0 && {item.lastPrice}} + {activeIndex === 0 && {dayjs(Number(item.tradeTime)).format('YYYY-MM-DD HH:mm:ss')}} + {activeIndex !== 0 && {item.updatedAt}} {item.status ? item.status : t('success')} lookDetail(item)}>{t('Detail')} diff --git a/src/views/Bazaar/hooks/index.ts b/src/views/Bazaar/hooks/index.ts index 63cd1ec..f559168 100644 --- a/src/views/Bazaar/hooks/index.ts +++ b/src/views/Bazaar/hooks/index.ts @@ -1,8 +1,20 @@ import { useCallback } from 'react' -import { useBlindBox } from 'hooks/useContract' -import { getAddress, getBlindBoxAddress } from 'utils/addressHelpers' -import { getOfficialPage, getOfficialPurchase, getPurchaseRecord, getNftDetail } from 'services/bazaar' +import hccMarketplaceABI from 'config/abi/hccMarketplace.json' +import blindBoxABI from 'config/abi/blindBox.json' +import { useBlindBox, useHccMarketplace } from 'hooks/useContract' +import { getAddress, getBlindBoxAddress, getHccMarketplaceAddress } from 'utils/addressHelpers' +import { useAccount } from 'state/userInfo/hooks' +import { getOfficialPage, getOfficialPurchase, getPurchaseRecord, getNftDetail, getTradePage } from 'services/bazaar' import { ethers, Contract } from 'ethers' +import multicall from 'utils/multicall' +import { + getBalanceNumber, + getDecimalAmount, + formatDivNumber, + getDecimalAmountNumber, + formatTimeNumber, +} from 'utils/formatBalance' +import BigNumber from 'bignumber.js' export const useGetOfficialPage = () => { const data = async (params) => { @@ -39,6 +51,19 @@ export const useApproveHcc = (tokenContract: Contract) => { return { onApprove: handleApprove } } +export const useMarketplaceApproveHcc = (tokenContract: Contract) => { + const handleApprove = useCallback(async () => { + try { + const tx = await tokenContract.approve(getHccMarketplaceAddress(), ethers.constants.MaxUint256) + const receipt = await tx.wait() + return receipt.status + } catch (e) { + return false + } + }, [tokenContract]) + + return { onApprove: handleApprove } +} // 交易记录 export const useGetPurchaseRecord = () => { @@ -74,4 +99,212 @@ export const useGetNftDetail = () => { return data } +// 市场分页 +export const useGetTradePage = () => { + const data = async (params) => { + const result = await getTradePage(params) + console.log(result) + return result + } + return data +} + +// 是否授权合约 +export const useIsApproved = () => { + const account = useAccount() + const blindBox = useBlindBox() + const address = getBlindBoxAddress() + const hccMarketplaceAddress = getHccMarketplaceAddress() + const transaction = async (tokenId) => { + const mintParams = [tokenId] + + const calls = [ + { + address, + name: 'getApproved', + params: [tokenId], + }, + ] + + const res = await multicall(blindBoxABI, calls) + // const res = await blindBox.getApproved(...mintParams) + console.log(res[0][0].toLowerCase()) + console.log(hccMarketplaceAddress) + let status = false + if (res[0][0].toLowerCase() === hccMarketplaceAddress.toLowerCase()) { + status = true + } + return status + } + return transaction +} +// approve +export const useApproved = () => { + const account = useAccount() + const blindBox = useBlindBox() + const address = getBlindBoxAddress() + const hccMarketplaceAddress = getHccMarketplaceAddress() + const transaction = async (tokenId) => { + const mintParams = [hccMarketplaceAddress, tokenId] + console.log(mintParams) + // const calls = [ + // { + // address, + // name: 'getApproved', + // params: [tokenId], + // }, + // ] + + // const res = await multicall(blindBoxABI, calls) + const res = await blindBox.approve(...mintParams) + console.log(res) + return res + } + return transaction +} + +// 一口价 +export const useCreateNormalOrder = () => { + const hccMarketplace = useHccMarketplace() + const transaction = async (tokenId, price) => { + const hccMarketplaceAddress = getHccMarketplaceAddress() + const calls = [ + { + address: hccMarketplaceAddress, + name: 'createNormalOrder', + params: [tokenId, getDecimalAmount(price).toString()], + }, + ] + const mintParams = [tokenId, getDecimalAmount(price).toString()] + console.log(mintParams) + const res = await hccMarketplace.createNormalOrder(...mintParams) + + // const res = await multicall(hccMarketplaceABI, calls) + console.log(res) + return res + } + return transaction +} + +// 拍卖 +export const useCreateAuctionOrder = () => { + const hccMarketplace = useHccMarketplace() + const transaction = async (tokenId, price, time) => { + const hccMarketplaceAddress = getHccMarketplaceAddress() + // const calls = [ + // { + // address: hccMarketplaceAddress, + // name: 'createAuctionOrder', + // params: [account], + // }, + // ] + console.log(hccMarketplace) + const mintParams = [tokenId, getDecimalAmount(price).toString(), time] + console.log(mintParams) + const res = await hccMarketplace.createAuctionOrder(...mintParams) + // const res = await multicall(hccMarketplaceABI, calls) + console.log(res) + return res + } + return transaction +} + +// 取消挂单 +export const useCancelOrder = () => { + const hccMarketplace = useHccMarketplace() + const transaction = async (id) => { + const hccMarketplaceAddress = getHccMarketplaceAddress() + // const calls = [ + // { + // address: hccMarketplaceAddress, + // name: 'createAuctionOrder', + // params: [account], + // }, + // ] + + const mintParams = [id] + console.log(...mintParams) + const res = await hccMarketplace.cancelOrder(...mintParams) + // const res = await multicall(hccMarketplaceABI, calls) + console.log(res) + return res + } + return transaction +} + +// 一口价购买 +export const useExecuteOrder = () => { + const hccMarketplace = useHccMarketplace() + const transaction = async (tokenId, price) => { + const hccMarketplaceAddress = getHccMarketplaceAddress() + // const calls = [ + // { + // address: hccMarketplaceAddress, + // name: 'createAuctionOrder', + // params: [account], + // }, + // ] + + const mintParams = [tokenId, getDecimalAmount(price).toString()] + console.log(...mintParams) + const res = await hccMarketplace.executeOrder(...mintParams) + // const res = await multicall(hccMarketplaceABI, calls) + console.log(res) + return res + } + return transaction +} +// 竞价 +export const useBidAuctionOrder = () => { + const hccMarketplace = useHccMarketplace() + const transaction = async (tokenId, price) => { + const hccMarketplaceAddress = getHccMarketplaceAddress() + // const calls = [ + // { + // address: hccMarketplaceAddress, + // name: 'createAuctionOrder', + // params: [account], + // }, + // ] + + const mintParams = [tokenId, getDecimalAmount(price).toString()] + console.log(...mintParams) + const res = await hccMarketplace.bidAuctionOrder(...mintParams) + // const res = await multicall(hccMarketplaceABI, calls) + console.log(res) + return res + } + return transaction +} + +export const useOrderMap = () => { + const hccMarketplace = useHccMarketplace() + const transaction = async (tokenId) => { + const hccMarketplaceAddress = getHccMarketplaceAddress() + const calls = [ + { + address: hccMarketplaceAddress, + name: 'orderMap', + params: [tokenId], + }, + ] + const res = await multicall(hccMarketplaceABI, calls) + const arr = res.map((item) => { + console.log(item) + console.log(new BigNumber(item.expiresAt._hex).toNumber()) + return { + price: getBalanceNumber(item.price._hex), + expiresAt: new BigNumber(item.expiresAt._hex).toNumber(), + bidder: + new BigNumber(item.bidder).toString().toLowerCase() === 'nan' + ? item.bidder + : new BigNumber(item.bidder).toString().toLowerCase(), + bid: getBalanceNumber(item.bid._hex), + } + }) + return arr[0] + } + return transaction +} + export default useGetOfficialPage diff --git a/src/views/Ido/components/HeaderStatus.tsx b/src/views/Ido/components/HeaderStatus.tsx index 85785ed..d03583e 100644 --- a/src/views/Ido/components/HeaderStatus.tsx +++ b/src/views/Ido/components/HeaderStatus.tsx @@ -38,7 +38,7 @@ const HeaderStatus: React.FC = ({ status, roundDetail }) => { return ( - {/* {status === 'close' && t('Exchange closed')} */} + {status === 'close' && t('Exchange closed')} {status === 'none' && t('Exchange not commenced')} {status === 'proceed' && ( diff --git a/src/views/Ido/hooks/index.ts b/src/views/Ido/hooks/index.ts index 04cb942..a958656 100644 --- a/src/views/Ido/hooks/index.ts +++ b/src/views/Ido/hooks/index.ts @@ -68,8 +68,6 @@ export const useGetRound = () => { } const { id } = data const idoPurchaseAddress = getIdoPurchaseAddress() - console.log(idoPurchaseAddress) - console.log(id) const calls = [ { address: idoPurchaseAddress, @@ -91,6 +89,7 @@ export const useGetRound = () => { total: formatDivNumber(item?.total._hex, 18), } }) + console.log(detail[0]) return detail[0] } return transaction diff --git a/src/views/Ido/index.tsx b/src/views/Ido/index.tsx index 268e60b..40a38fd 100644 --- a/src/views/Ido/index.tsx +++ b/src/views/Ido/index.tsx @@ -41,10 +41,17 @@ const Exchange: React.FC = () => { const getRound = useGetRound() const init = async () => { const detail = await getRound() + console.log(typeof detail.remaining) if (!detail || detail.endTime < new Date().getTime()) { setStatus('none') - } else if (detail.beginTime < new Date().getTime() && detail.endTime > new Date().getTime()) { + } else if ( + detail.beginTime < new Date().getTime() && + detail.endTime > new Date().getTime() && + detail.remaining > 0 + ) { setStatus('proceed') + } else if (detail.remaining === 0) { + setStatus('close') } else { setStatus('end') } diff --git a/src/views/NftBox/components/DetailModal.tsx b/src/views/NftBox/components/DetailModal.tsx index 77fa6d1..1064c4f 100644 --- a/src/views/NftBox/components/DetailModal.tsx +++ b/src/views/NftBox/components/DetailModal.tsx @@ -1,9 +1,10 @@ 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 { Modal, Flex, Text, Image, Button, useModal } from '@pancakeswap/uikit' import { useGetNftDetails } from '../hooks' import AssetsInfo from './AssetsInfo' +import SellModal from '../../Bazaar/components/SellModal' interface DetailProp { token?: string @@ -133,7 +134,11 @@ const DetailFlex = styled(Flex)` const SellButton = styled(Button)` height: 60px; background: linear-gradient(269deg, #1fc8d3 0%, #1fd4b0 100%); - opacity: 1; + border-radius: 30px; + margin-top: 30px; +` +const HaveButton = styled(Button)` + height: 60px; border-radius: 30px; margin-top: 30px; ` @@ -156,6 +161,8 @@ const DetailModal: React.FC = ({ token, onDismiss }) => { getDetail() }, [token]) + const [onSellModal] = useModal() + return (
@@ -171,7 +178,8 @@ const DetailModal: React.FC = ({ token, onDismiss }) => { - {t('Selling immediately')} + {!detail?.pending && {t('Selling immediately')}} + {detail?.pending && {t('Have they')}} diff --git a/src/views/NftBox/components/ShopList.tsx b/src/views/NftBox/components/ShopList.tsx index 4c8e909..fc297e7 100644 --- a/src/views/NftBox/components/ShopList.tsx +++ b/src/views/NftBox/components/ShopList.tsx @@ -65,6 +65,7 @@ interface Detail { info: InfoProps token?: string updatedAt?: string + pending?: boolean } interface InfoProps { coverResource?: CoverResourceProps