🐞 fix:

This commit is contained in:
john 2024-07-02 18:41:27 +08:00
parent f6cd16f1c6
commit aff0f68989
24 changed files with 768 additions and 347 deletions

View File

@ -1,12 +1,13 @@
###
# @LastEditors: John
# @Date: 2024-06-18 10:12:21
# @LastEditTime: 2024-06-27 15:35:23
# @LastEditTime: 2024-07-02 16:46:51
# @Author: John
###
VITE_BASE_URL=
VITE_BASE_API_URL=/dev
VITE_PARTICIPATE_CHAIN_ID=97
VITE_PURCHASED_CONTRACT_ADDRESS=0x7aAe4f2CA23482B58D6f9e8d1fBb5e413e7013c8
VITE_RECEIVE_RAMB_CONTRACT_ADDRESS=0x8291A98382d751CdD52460A547eE94ceE8258930
VITE_NETWORK_USDT_ADDRESS=0xf9A18B7FC8Eb118f8Ad59fBD6eb1A181eaCb4E63
VITE_CHECK_TRANSACTION_DETAILS_URL=https://testnet.bscscan.com/

View File

@ -1,12 +1,13 @@
###
# @LastEditors: John
# @Date: 2024-06-24 18:38:45
# @LastEditTime: 2024-06-25 14:04:37
# @LastEditTime: 2024-07-02 16:49:31
# @Author: John
###
VITE_BASE_URL=http://wwwtest.exgo.pro
VITE_BASE_API_URL=http://wwwtest.exgo.pro
VITE_PARTICIPATE_CHAIN_ID=97
VITE_PURCHASED_CONTRACT_ADDRESS=0x7aAe4f2CA23482B58D6f9e8d1fBb5e413e7013c8
VITE_RECEIVE_RAMB_CONTRACT_ADDRESS=0x8291A98382d751CdD52460A547eE94ceE8258930
VITE_NETWORK_USDT_ADDRESS=0xf9A18B7FC8Eb118f8Ad59fBD6eb1A181eaCb4E63
VITE_CHECK_TRANSACTION_DETAILS_URL=https://testnet.bscscan.com/

View File

@ -1,5 +1,5 @@
{
"symbol_url": "//at.alicdn.com/t/c/font_4589567_vzk80gocu8s.js",
"symbol_url": "//at.alicdn.com/t/c/font_4589567_u5yhe33hzt.js",
"use_typescript": true,
"save_dir": "./src/components/iconfont",
"trim_icon_prefix": "icon",

View File

@ -19,7 +19,7 @@ import InvitationList from "./pages/InvitationList";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import useUserStore from "./store/User";
import { getUrlQueryParam } from "./utils";
import { getUrlParameterByName } from "./utils";
import { UrlQueryParamsKey } from "./constants";
import { signAndLogin } from "./utils/wallet";
import { useAccount } from "wagmi";
@ -29,7 +29,9 @@ function App() {
const { address } = useAccount();
useEffect(() => {
i18n.changeLanguage(currantLang);
UpdateInviteCode(getUrlQueryParam(UrlQueryParamsKey.INVITE_CODE) || "");
UpdateInviteCode(
getUrlParameterByName(UrlQueryParamsKey.INVITE_CODE) || ""
);
return () => {};
}, []);

View File

@ -0,0 +1,33 @@
/* tslint:disable */
/* eslint-disable */
import React, { CSSProperties, SVGAttributes, FunctionComponent } from 'react';
import { getIconColor } from './helper';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
const DEFAULT_STYLE: CSSProperties = {
display: 'block',
};
const IconTransfer: FunctionComponent<Props> = ({ size = 18, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M85.346462 170.653538c0-47.104 38.203077-85.307077 85.307076-85.307076H512c47.143385 0 85.346462 38.203077 85.346462 85.307076v170.692924c0 47.104-38.203077 85.307077-85.346462 85.307076H170.653538a85.346462 85.346462 0 0 1-85.307076-85.307076V170.653538zM426.653538 682.653538c0-47.104 38.203077-85.307077 85.346462-85.307076h341.346462c47.104 0 85.307077 38.203077 85.307076 85.307076v170.692924c0 47.104-38.203077 85.307077-85.307076 85.307076H512a85.346462 85.346462 0 0 1-85.346462-85.307076v-170.692924z"
fill={getIconColor(color, 0, '#666666')}
/>
<path
d="M879.064615 287.980308l-30.72 30.72a32.019692 32.019692 0 0 0 45.292308 45.292307l55.138462-55.177846c29.184-29.144615 29.184-76.445538 0-105.629538l-55.138462-55.138462a32.019692 32.019692 0 0 0-45.292308 45.252923l30.72 30.72h-196.411077a32.019692 32.019692 0 0 0 0 63.960616h196.450462zM144.935385 799.980308l30.72 30.72a32.019692 32.019692 0 0 1-45.292308 45.292307l-55.138462-55.177846c-29.184-29.144615-29.184-76.445538 0-105.629538l55.138462-55.138462a32.019692 32.019692 0 0 1 45.292308 45.252923l-30.72 30.72h196.411077a32.019692 32.019692 0 0 1 0 63.960616H144.896z"
fill={getIconColor(color, 1, '#FFFFFF')}
/>
</svg>
);
};
export default IconTransfer;

View File

@ -2,6 +2,7 @@
/* eslint-disable */
import React, { SVGAttributes, FunctionComponent } from 'react';
import IconTransfer from './IconTransfer';
import IconDiqiu from './IconDiqiu';
import IconTuichu from './IconTuichu';
import IconChevronsrightshuangyoujiantou from './IconChevronsrightshuangyoujiantou';
@ -11,6 +12,7 @@ import IconTongdun from './IconTongdun';
import IconJindun from './IconJindun';
import IconXingdun from './IconXingdun';
import IconGuanjun from './IconGuanjun';
export { default as IconTransfer } from './IconTransfer';
export { default as IconDiqiu } from './IconDiqiu';
export { default as IconTuichu } from './IconTuichu';
export { default as IconChevronsrightshuangyoujiantou } from './IconChevronsrightshuangyoujiantou';
@ -21,7 +23,7 @@ export { default as IconJindun } from './IconJindun';
export { default as IconXingdun } from './IconXingdun';
export { default as IconGuanjun } from './IconGuanjun';
export type IconNames = 'diqiu' | 'tuichu' | 'chevronsrightshuangyoujiantou' | 'fuzhi' | 'icon_arrow_left' | 'tongdun' | 'jindun' | 'xingdun' | 'guanjun';
export type IconNames = 'transfer' | 'diqiu' | 'tuichu' | 'chevronsrightshuangyoujiantou' | 'fuzhi' | 'icon_arrow_left' | 'tongdun' | 'jindun' | 'xingdun' | 'guanjun';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
name: IconNames;
@ -31,6 +33,8 @@ interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
const IconFont: FunctionComponent<Props> = ({ name, ...rest }) => {
switch (name) {
case 'transfer':
return <IconTransfer {...rest} />;
case 'diqiu':
return <IconDiqiu {...rest} />;
case 'tuichu':

View File

@ -0,0 +1,173 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "payAddr",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "paymentTime",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "orderId",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "hashStr",
"type": "bytes32"
}
],
"name": "reward",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "buyAddr",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "orderId",
"type": "uint256"
}
],
"name": "RewardSuccess",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "addr",
"type": "address"
}
],
"name": "setUSDCAddress",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "withdraw",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "usdc",
"outputs": [
{
"internalType": "contract IERC20",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]

View File

@ -1,7 +1,7 @@
/*
* @LastEditors: John
* @Date: 2024-06-19 15:48:57
* @LastEditTime: 2024-06-27 18:00:07
* @LastEditTime: 2024-07-02 17:40:49
* @Author: John
*/
import { config } from "@/components/WalletProvider";
@ -16,8 +16,10 @@ import { encodeFunctionData } from "viem/utils";
import erc20Abi from "@/contract/abi/erc20abi.json";
import usdtAbi from "@/contract/abi/USDT.json";
import RedDevilsAbi from "@/contract/abi/RedDevils.json";
import receiveAbi from "@/contract/abi/receive.json";
import i18next from "i18next";
import { BaseError } from "wagmi";
import { UserIncome } from "@/server/module";
/**
* @description
@ -270,19 +272,76 @@ export async function upGradeByContract(amount: bigint, orderID: string) {
}
/**
* receiveByContract
* receiveRMABByContract
* @param amount
* @param paymentTime
* @param orderID
* @param hashStr
* @returns
*/
export async function receiveByContract(
export async function receiveRMABByContract(
amount: bigint,
paymentTime: number,
orderID: string,
hashStr: string
) {
console.log("pay buy contract params", { amount, orderID });
return new Promise<string>(async (reslove, reject) => {
try {
console.log("参数:", amount, paymentTime, orderID, hashStr);
estimateGas(config, {
to: import.meta.env.VITE_RECEIVE_RAMB_CONTRACT_ADDRESS,
data: encodeFunctionData({
abi: receiveAbi,
functionName: "reward",
args: [amount, paymentTime, orderID, hashStr],
}),
})
.then((gas) => {
const gasPrice = (gas * 12n) / 10n;
console.log("estimate gas:%d , my gas: %d", gas, gasPrice);
writeContract(config, {
abi: receiveAbi,
address: import.meta.env.VITE_PURCHASED_CONTRACT_ADDRESS,
functionName: "reward",
args: [amount, paymentTime, orderID, hashStr],
gas: gasPrice,
})
.then((receipt) => {
console.log("write contract success!, receipt:", receipt);
reslove(receipt);
})
.catch((err: BaseError) => {
console.log("reward rmab Transaction err", err);
reject(err);
});
})
.catch((err: BaseError) => {
console.log("reward rmab estimateGas err", err);
reject(err);
});
} catch (err) {
reject(new BaseError(`${err}`));
}
});
}
/**
* receiveUSDTByContract
* @param amount
* @param paymentTime
* @param orderID
* @param hashStr
* @returns
*/
export async function receiveUSDTByContract(
amount: bigint,
paymentTime: number,
orderID: string,
hashStr: string
) {
console.log("pay buy contract params", { amount, orderID });
console.log("NETWORK_USDT:", import.meta.env.VITE_NETWORK_USDT_ADDRESS);
return new Promise<string>(async (reslove, reject) => {
try {
@ -323,3 +382,26 @@ export async function receiveByContract(
}
});
}
/**
* receiveByContract
* @param type
* @param amount
* @param paymentTime
* @param orderID
* @param hashStr
* @returns
*/
export async function receiveByContract(
type: UserIncome["coinId"],
amount: bigint,
paymentTime: number,
orderID: string,
hashStr: string
) {
if (type == 1) {
return receiveUSDTByContract(amount, paymentTime, orderID, hashStr);
} else if (type == 2) {
return receiveRMABByContract(amount, paymentTime, orderID, hashStr);
}
}

View File

@ -104,5 +104,7 @@
"没有更多数据了": "没有更多数据了",
"升级中": "升级中",
"无等级": "无等级",
"无效的邀请链接": "无效的邀请链接"
"无效的邀请链接": "无效的邀请链接",
"交易红魔股权NFT": "交易红魔股权NFT"
}

View File

@ -104,5 +104,6 @@
"没有更多数据了": "Keine weiteren Daten",
"升级中": "Wird aktualisiert",
"无等级": "Keine Bewertung",
"无效的邀请链接": "Ungültiger Einladungslink"
"无效的邀请链接": "Ungültiger Einladungslink",
"交易红魔股权NFT": "Handel mit Red Devil Equity NFT"
}

View File

@ -104,5 +104,6 @@
"没有更多数据了": "No more data",
"升级中": "Upgrading",
"无等级": "No Grade",
"无效的邀请链接": "Invalid invitation link"
"无效的邀请链接": "Invalid invitation link",
"交易红魔股权NFT": "Trade Red Devil Equity NFT"
}

View File

@ -104,5 +104,6 @@
"没有更多数据了": "これ以上のデータはありません",
"升级中": "アップグレード中",
"无等级": "グレードなし",
"无效的邀请链接": "無効な招待リンクです"
"无效的邀请链接": "無効な招待リンクです",
"交易红魔股权NFT": "レッドデビル株式NFTを取引する"
}

View File

@ -104,5 +104,6 @@
"没有更多数据了": "沒有更多數據了",
"升级中": "升級中",
"无等级": "無等級",
"无效的邀请链接": "無效的邀請連結"
"无效的邀请链接": "無效的邀請連結",
"交易红魔股权NFT": "交易紅魔股權NFT"
}

View File

@ -1,7 +1,7 @@
/*
* @LastEditors: John
* @Date: 2024-06-17 17:20:03
* @LastEditTime: 2024-06-20 11:43:14
* @LastEditTime: 2024-07-02 11:38:25
* @Author: John
*/
import "@/i18n/init.ts";
@ -15,9 +15,9 @@ import EventBusProvider from "./context/EventBusContext.tsx";
import { WalletProvider } from "./components/WalletProvider.tsx";
import flexible from "./utils/flexible.ts";
import VConsole from "vconsole";
import { getUrlQueryParam } from "./utils/index.ts";
import { getUrlParameterByName } from "./utils/index.ts";
if (getUrlQueryParam("vconsole") === "1") {
if (getUrlParameterByName("vconsole") === "1") {
new VConsole();
}
flexible(window, document);

View File

@ -1,12 +1,12 @@
/*
* @LastEditors: John
* @Date: 2024-06-18 17:57:13
* @LastEditTime: 2024-06-25 16:17:20
* @LastEditTime: 2024-07-02 17:43:52
* @Author: John
*/
import Tabs from "antd-mobile/es/components/tabs";
import classes from "./AssetRecord.module.css";
import { cn, getUrlQueryParam } from "@/utils";
import { cn, getUrlParameterByName } from "@/utils";
import CapsuleTabs from "antd-mobile/es/components/capsule-tabs";
import RecordsItem from "@/components/RecordsItem";
import { useTranslation } from "react-i18next";
@ -14,12 +14,11 @@ import { useEffect, useMemo, useRef, useState } from "react";
import { api_pagling_query_income_record } from "@/server/api";
import { IncomeRecord, IncomeRecordType } from "@/server/module";
import { Empty, InfiniteScroll } from "antd-mobile";
import { CoinName } from "@/constants";
export default function () {
const { t } = useTranslation();
const coinId = useMemo(() => getUrlQueryParam("id"), []);
const coinName = useMemo(() => getUrlQueryParam("name"), []);
const id = useMemo(() => getUrlParameterByName("id"), []);
const coinId = useMemo(() => getUrlParameterByName("coinId"), []);
const currentType = useRef<1 | 2>(2);
const [issueRecords, setIssueRecords] = useState<IncomeRecord["records"]>([]);
const [receiveRecord, setReceiveRecord] = useState<IncomeRecord["records"]>(
@ -35,7 +34,7 @@ export default function () {
async function getRecord() {
return new Promise<void>(async (reslove) => {
if (!coinId) return;
if (!id) return;
if (!hasMore.current) return;
@ -43,7 +42,7 @@ export default function () {
pageNum.current++;
const { data } = await api_pagling_query_income_record().send({
queryParams: {
id: coinId,
id,
type: currentType.current,
pageNum: pageNum.current,
pageSize,
@ -91,7 +90,7 @@ export default function () {
}}
>
<Tabs.Tab className={classes.tab} title={t("发放记录")} key="1">
{coinName == CoinName.USDT && (
{coinId == "1" && (
<CapsuleTabs
onChange={(key) => {
switch (key) {
@ -117,7 +116,7 @@ export default function () {
</CapsuleTabs>
)}
{coinName == CoinName.RMOB && (
{coinId == "2" && (
<CapsuleTabs
onChange={(key) => {
switch (key) {

View File

@ -204,7 +204,7 @@
flex-direction: row;
align-items: center;
li {
width: 76px;
min-width: 76px;
height: 34px;
opacity: 1;
@ -220,6 +220,7 @@
color: #4d4d4d;
text-align: center;
line-height: 34px;
padding: 0 10px;
&.nftToken_tab_active {
border-radius: 10px;
opacity: 1;
@ -585,6 +586,43 @@
}
}
.equityNft {
display: flex;
flex-direction: row;
align-items: center;
gap: 16px;
padding: 10px 15px;
border-radius: 14px;
opacity: 1;
background: #171719;
margin-top: 26px;
> svg {
width: 26px;
height: 26px;
&:nth-of-type(2) {
margin-left: auto;
}
}
span {
opacity: 1;
font-family: DM Sans;
font-size: 14px;
font-weight: 500;
line-height: normal;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #ffffff;
z-index: 1;
}
}
.invite {
margin-top: 25px;
.invite_top {

View File

@ -10,17 +10,16 @@ import usdtBg from "@/assets/usdt_bg.svg";
import RMOB_logo from "@/assets/RMOB_logo.svg";
import IconFont from "@/components/iconfont";
import { BaseError, useAccount } from "wagmi";
import { config } from "@/components/WalletProvider";
import { createSearchParams, useNavigate } from "react-router-dom";
import { Button, Dialog, Empty, Toast } from "antd-mobile";
import { useNavigate } from "react-router-dom";
import { Button, Dialog, Empty, PullToRefresh, Toast } from "antd-mobile";
import { loginOut } from "@/utils/wallet";
import { api_claim_income, api_get_homepage_user_data } from "@/server/api";
import { UserHomeData } from "@/server/module";
import { UserHomeData, UserIncome } from "@/server/module";
import { UrlQueryParamsKey } from "@/constants";
import { receiveByContract } from "@/contract/utils";
import usePollingCheckBuyStatus from "@/hook/usePollingCheckBuyStatus";
import { ToastHandler } from "antd-mobile/es/components/toast";
import { disconnect, getAccount } from "@wagmi/core";
import { PullStatus } from "antd-mobile/es/components/pull-to-refresh";
export default function () {
const { Token, UpdateToken } = useUserStore();
const { open } = useWeb3Modal();
@ -30,7 +29,12 @@ export default function () {
const [tabIndex, setTabIndex] = useState(0);
const navigate = useNavigate();
const [userData, setUserData] = useState<UserHomeData>();
const statusRecord: Record<PullStatus, string> = {
pulling: "Pull down to refresh",
canRelease: "Release to refresh immediately",
refreshing: "Loading...",
complete: "Refresh complete",
};
const userInviteLink = useMemo(
() =>
`${location.origin}/#/?${UrlQueryParamsKey.INVITE_CODE}=${
@ -76,334 +80,369 @@ export default function () {
return (
<>
<div className={cn(classes.Home, classes.container)}>
<div className={classes.userinfo}>
<div className={classes.userinfo_top}>
<img className={classes.userinfo_top_left} src={logo} alt="" />
<PullToRefresh
onRefresh={async () => {
await getHomeData();
}}
renderText={(status) => {
return <div>{statusRecord[status]}</div>;
}}
disabled={!Token}
>
<div className={cn(classes.Home, classes.container)}>
<div className={classes.userinfo}>
<div className={classes.userinfo_top}>
<img
className={classes.userinfo_top_left}
src={userData?.userImg || logo}
alt=""
/>
{address ? (
<div className={classes.userinfo_top_right}>
<div className={classes.userinfo_top_right_wallet}>
<span>{shortenString(address, 6, 4)}</span>
<IconFont
onClick={async () => {
loginOut();
{address ? (
<div className={classes.userinfo_top_right}>
<div className={classes.userinfo_top_right_wallet}>
<span>{shortenString(address, 6, 4)}</span>
<IconFont
onClick={async () => {
loginOut();
}}
name="tuichu"
className={classes.userinfo_top_right_wallet_disconnect}
color={"#fff"}
/>
</div>
<div className={classes.userinfo_top_right_btns}>
{userData && (
<>
<div className={classes.userinfo_top_right_btns_item}>
{userData.level == 0 && (
<>
<IconFont
name="tongdun"
className={classes.userinfo_top_right_btns_icon}
/>
<span>{t("无等级")}</span>
</>
)}
{userData.level == 1 && (
<>
<IconFont
name="jindun"
className={classes.userinfo_top_right_btns_icon}
/>
{userData.active === 0 && (
<span>{t("普通非活跃")}</span>
)}
{userData.active === 1 && (
<span>{t("普通活跃")}</span>
)}
</>
)}
{userData.level == 2 && (
<>
<IconFont
name="xingdun"
className={classes.userinfo_top_right_btns_icon}
/>
<span>{t("社长")}</span>
</>
)}
{userData.level == 3 && (
<>
<IconFont
name="guanjun"
className={classes.userinfo_top_right_btns_icon}
/>
<span>{t("基金会社长")}</span>
</>
)}
</div>
<div
className={classes.userinfo_top_right_btns_item}
onClick={() => {
navigate("/levelup");
}}
>
<span>{t("升级")}</span>
<IconFont
name="chevronsrightshuangyoujiantou"
className={classes.userinfo_top_right_btns_icon}
color={"#fff"}
/>
</div>
</>
)}
</div>
</div>
) : (
<>
<div
className={classes.userinfo_top_right_connect}
onClick={() => {
open();
}}
name="tuichu"
className={classes.userinfo_top_right_wallet_disconnect}
color={"#fff"}
/>
</div>
<div className={classes.userinfo_top_right_btns}>
{userData && (
<>
<div className={classes.userinfo_top_right_btns_item}>
{userData.level == 0 && (
<>
<IconFont
name="tongdun"
className={classes.userinfo_top_right_btns_icon}
/>
<span>{t("无等级")}</span>
</>
)}
{userData.level == 1 && (
<>
<IconFont
name="jindun"
className={classes.userinfo_top_right_btns_icon}
/>
{userData.active === 0 && (
<span>{t("普通非活跃")}</span>
)}
{userData.active === 1 && (
<span>{t("普通活跃")}</span>
)}
</>
)}
{userData.level == 2 && (
<>
<IconFont
name="xingdun"
className={classes.userinfo_top_right_btns_icon}
/>
<span>{t("社长")}</span>
</>
)}
{userData.level == 3 && (
<>
<IconFont
name="guanjun"
className={classes.userinfo_top_right_btns_icon}
/>
<span>{t("基金会社长")}</span>
</>
)}
</div>
<div
className={classes.userinfo_top_right_btns_item}
onClick={() => {
navigate("/levelup");
}}
>
<span>{t("升级")}</span>
<IconFont
name="chevronsrightshuangyoujiantou"
className={classes.userinfo_top_right_btns_icon}
color={"#fff"}
/>
</div>
</>
)}
</div>
</div>
) : (
<>
<div
className={classes.userinfo_top_right_connect}
onClick={() => {
open();
}}
>
<span>{t("链接钱包")}</span>
</div>
</>
)}
>
<span>{t("链接钱包")}</span>
</div>
</>
)}
</div>
<ul className={classes.userinfo_data}>
<li>
<span className={classes.userinfo_data_num}>
{userData?.mintNumber || 0}
</span>
<span className={classes.userinfo_data_des}>
{t("邀请铸造")}
</span>
</li>
<li>
<span className={classes.userinfo_data_num}>
{userData?.presidentNumber || 0}
</span>
<span className={classes.userinfo_data_des}>
{t("团队社长")}
</span>
</li>
<li>
<span className={classes.userinfo_data_num}>
{userData?.airdropNumber || 0}
</span>
<span className={classes.userinfo_data_des}>
{t("邀请空投")}
</span>
</li>
</ul>
</div>
<ul className={classes.userinfo_data}>
<li>
<span className={classes.userinfo_data_num}>
{userData?.mintNumber || 0}
</span>
<span className={classes.userinfo_data_des}>{t("邀请铸造")}</span>
</li>
<li>
<span className={classes.userinfo_data_num}>
{userData?.presidentNumber || 0}
</span>
<span className={classes.userinfo_data_des}>{t("团队社长")}</span>
</li>
<li>
<span className={classes.userinfo_data_num}>
{userData?.airdropNumber || 0}
</span>
<span className={classes.userinfo_data_des}>{t("邀请空投")}</span>
</li>
</ul>
</div>
<div className={classes.nftToken}>
<ul className={classes.nftToken_tab}>
<li
className={tabIndex == 0 ? classes.nftToken_tab_active : ""}
onClick={() => setTabIndex(0)}
>
NFT
</li>
<li
className={tabIndex == 1 ? classes.nftToken_tab_active : ""}
onClick={() => setTabIndex(1)}
>
{t("收益")}
</li>
</ul>
<div className={classes.nftToken}>
<ul className={classes.nftToken_tab}>
<li
className={tabIndex == 0 ? classes.nftToken_tab_active : ""}
onClick={() => setTabIndex(0)}
>
NFT
</li>
<li
className={tabIndex == 1 ? classes.nftToken_tab_active : ""}
onClick={() => setTabIndex(1)}
>
{t("收益")}
</li>
</ul>
<div className={classes.nftToken_content}>
{tabIndex == 0 && (
<>
{address ? (
<>
{userData?.nftId ? (
<div className={classes.nftToken_content_nft}>
<div className={classes.nftToken_content_nft_top}>
<span># {userData?.nftId}</span>
<span
<div className={classes.nftToken_content}>
{tabIndex == 0 && (
<>
{address ? (
<>
{userData?.nftId ? (
<div className={classes.nftToken_content_nft}>
<div className={classes.nftToken_content_nft_top}>
<span># {userData?.nftId}</span>
<span
onClick={() => {
navigate("/mint");
}}
>
{t("铸造 NFT")}
<IconFont
name="chevronsrightshuangyoujiantou"
color={"#F3BE3C"}
/>
</span>
</div>
<img
className={classes.nftToken_content_nft_img}
src={nftBg}
alt=""
/>
<span className={classes.nftToken_content_nft_des}>
{t("Min结束后按照规则进行空投。")}
</span>
</div>
) : (
<div className={classes.nftToken_content_nft_mint}>
<div
className={classes.nftToken_content_nft_mint_btn}
onClick={() => {
navigate("/mint");
}}
>
{t("铸造 NFT")}
<span>{t("铸造 NFT")}</span>
<IconFont
className={
classes.nftToken_content_nft_mint_btn_icon
}
name="chevronsrightshuangyoujiantou"
color={"#F3BE3C"}
color={"#fff"}
/>
</span>
</div>
<span>{t("铸造 NFT 获得代币空投")}</span>
</div>
<img
className={classes.nftToken_content_nft_img}
src={nftBg}
alt=""
/>
<span className={classes.nftToken_content_nft_des}>
{t("Min结束后按照规则进行空投。")}
</span>
)}
</>
) : (
<>
<div className={classes.nftToken_content_userDisconnect}>
<span>{t("钱包未链接,无法向您显示 NFT")}</span>
</div>
) : (
<div className={classes.nftToken_content_nft_mint}>
<div
className={classes.nftToken_content_nft_mint_btn}
</>
)}
</>
)}
{tabIndex == 1 && (
<div className={classes.nftToken_content_token}>
<div className={classes.nftToken_content_token_top}>
<span>{t("总收益= 已领取 + 待领取")}</span>
</div>
<ul className={classes.nftToken_content_token_list}>
{userData?.userIncomes.map((v, i) => (
<ReceiveCom
key={i}
coinId={v.coinId}
tokenName={v.coinName}
tokenNum={v.receive}
toReceive={v.collection}
onAssetRec={() => {
navigate(
`/assetrecord?id=${v.id}&coinId=${v.coinId}`
);
}}
onReceive={async () => {
receiveLoadingToast.current = Toast.show({
icon: "loading",
duration: 0,
content: t("领取中"),
maskClickable: false,
});
const { data } = await api_claim_income().send({
queryParams: { id: v.id },
});
const orderInfo = data?.data;
if (!orderInfo?.orderNumber) return;
const buyAmount = BigInt(
orderInfo?.claimQuantity || ""
);
receiveByContract(
v.coinId,
buyAmount,
orderInfo.time,
orderInfo?.orderNumber,
orderInfo.hash
)
.then((hash) => {
if (!hash) return;
console.log("领取成功hash:", hash);
getHomeData();
startPollingCheckBuyStatus(hash);
})
.catch(async (err: BaseError) => {
receiveLoadingToast.current?.close();
Toast.show({
content: err.shortMessage,
icon: "fail",
});
});
}}
/>
))}
{(userData?.userIncomes.length == 0 ||
!userData?.userIncomes) && <Empty />}
</ul>
</div>
)}
</div>
</div>
<div
className={classes.equityNft}
onClick={() => window.open("https://element.market/account")}
>
<IconFont name="transfer" />
<span>{t("交易红魔股权NFT")}</span>
<IconFont name="chevronsrightshuangyoujiantou" color={"#FFFFFF"} />
</div>
<div className={classes.invite}>
<div className={classes.invite_top}>
<span>{t("邀请")}</span>
{address && (
<span
onClick={() => {
navigate("/invitationlist");
}}
>
{t("邀请列表")}{" "}
<IconFont
name="chevronsrightshuangyoujiantou"
color={"#F3BE3C"}
/>
</span>
)}
</div>
<div className={classes.invite_content}>
<span>{t("邀请链接")}</span>
<div className={classes.invite_content_link}>
{address ? (
<>
{userData?.nftId ? (
<>
<span>{shortenString(userInviteLink, 15, 15)}</span>
<IconFont
onClick={() => {
navigate("/mint");
copyText(userInviteLink);
}}
>
<span>{t("铸造 NFT")}</span>
<IconFont
className={
classes.nftToken_content_nft_mint_btn_icon
}
name="chevronsrightshuangyoujiantou"
color={"#fff"}
/>
</div>
<span>{t("铸造 NFT 获得代币空投")}</span>
</div>
className={classes.invite_content_icon}
name="fuzhi"
color={"#fff"}
/>{" "}
</>
) : (
<>
<span>{t("MINT Nft 获取邀请链接")}</span>
</>
)}
</>
) : (
<>
<div className={classes.nftToken_content_userDisconnect}>
<span>{t("钱包未链接,无法向您显示 NFT")}</span>
</div>
<span>{t("链接钱包获取邀请链接")}</span>
</>
)}
</>
)}
{tabIndex == 1 && (
<div className={classes.nftToken_content_token}>
<div className={classes.nftToken_content_token_top}>
<span>{t("总收益= 已领取 + 待领取")}</span>
</div>
<ul className={classes.nftToken_content_token_list}>
{userData?.userIncomes.map((v, i) => (
<ReceiveCom
key={i}
tokenName={v.coinName}
tokenNum={v.receive}
toReceive={v.collection}
onAssetRec={() => {
navigate(`/assetrecord?id=${v.id}&name=${v.coinName}`);
}}
onReceive={async () => {
receiveLoadingToast.current = Toast.show({
icon: "loading",
duration: 0,
content: t("领取中"),
maskClickable: false,
});
const { data } = await api_claim_income().send({
queryParams: { id: v.id },
});
const orderInfo = data?.data;
if (!orderInfo?.orderNumber) return;
const buyAmount = BigInt(
orderInfo?.claimQuantity || ""
);
receiveByContract(
buyAmount,
orderInfo.time,
orderInfo?.orderNumber,
orderInfo.hash
)
.then((hash) => {
console.log("领取成功hash:", hash);
getHomeData();
startPollingCheckBuyStatus(hash);
})
.catch(async (err: BaseError) => {
receiveLoadingToast.current?.close();
Toast.show({
content: err.shortMessage,
icon: "fail",
});
});
}}
/>
))}
{(userData?.userIncomes.length == 0 ||
!userData?.userIncomes) && <Empty />}
</ul>
</div>
)}
</div>
</div>
<div className={classes.invite}>
<div className={classes.invite_top}>
<span>{t("邀请")}</span>
{address && (
<span
onClick={() => {
navigate("/invitationlist");
}}
>
{t("邀请列表")}{" "}
<IconFont
name="chevronsrightshuangyoujiantou"
color={"#F3BE3C"}
/>
<span>
{t(
"普通会员每邀请铸造一个NFT可获得一份空投福利推荐铸造20个NFT的可升级为会长团队中拥有20位会长可升级为基金会社长邀请越多级别越高福利越多。"
)}
</span>
)}
</div>
<div className={classes.invite_content}>
<span>{t("邀请链接")}</span>
<div className={classes.invite_content_link}>
{address ? (
<>
{userData?.nftId ? (
<>
<span>{shortenString(userInviteLink, 15, 15)}</span>
<IconFont
onClick={() => {
copyText(userInviteLink);
}}
className={classes.invite_content_icon}
name="fuzhi"
color={"#fff"}
/>{" "}
</>
) : (
<>
<span>{t("MINT Nft 获取邀请链接")}</span>
</>
)}
</>
) : (
<>
<span>{t("链接钱包获取邀请链接")}</span>
</>
)}
</div>
</div>
<span>
{t(
"普通会员每邀请铸造一个NFT可获得一份空投福利推荐铸造20个NFT的可升级为会长团队中拥有20位会长可升级为基金会社长邀请越多级别越高福利越多。"
)}
</span>
<div className={classes.dataDisclosure}>
<span>{t("数据披露")}</span>
<ul className={classes.dataDisclosure_content}>
<li className={classes.dataDisclosure_content_item}>
<span>{t("资金池")}</span>
<span>{userData?.pools || 0}</span>
</li>
<li className={classes.dataDisclosure_content_item}>
<span>{t("社长席位")}</span>
<span>{userData?.president || 0}</span>
</li>
<li className={classes.dataDisclosure_content_item}>
<span>{t("基金会社长席位")}</span>
<span>{userData?.foundation || 0}</span>
</li>
</ul>
</div>
</div>
<div className={classes.dataDisclosure}>
<span>{t("数据披露")}</span>
<ul className={classes.dataDisclosure_content}>
<li className={classes.dataDisclosure_content_item}>
<span>{t("资金池")}</span>
<span>{userData?.pools || 0}</span>
</li>
<li className={classes.dataDisclosure_content_item}>
<span>{t("社长席位")}</span>
<span>{userData?.president || 0}</span>
</li>
<li className={classes.dataDisclosure_content_item}>
<span>{t("基金会社长席位")}</span>
<span>{userData?.foundation || 0}</span>
</li>
</ul>
</div>
</div>
</PullToRefresh>
</>
);
}
@ -414,18 +453,20 @@ function ReceiveCom({
toReceive,
onAssetRec,
onReceive,
coinId,
}: {
tokenName: string;
tokenNum: number;
toReceive: number;
onAssetRec: () => void;
onReceive: () => void;
coinId: UserIncome["coinId"];
}) {
const { t } = useTranslation();
return (
<li className={classes.nftToken_content_token_item}>
{tokenName.toUpperCase() == "USDT" && <img src={usdtBg} alt="" />}
{tokenName.toUpperCase() == "RMOB" && <img src={RMOB_logo} alt="" />}
{coinId == 1 && <img src={usdtBg} alt="" />}
{coinId == 2 && <img src={RMOB_logo} alt="" />}
<div>
<span className={classes.nftToken_content_token_item_tokenName}>
{tokenName}

View File

@ -1,7 +1,7 @@
/*
* @LastEditors: John
* @Date: 2024-06-18 10:28:21
* @LastEditTime: 2024-06-25 14:47:34
* @LastEditTime: 2024-07-02 11:36:40
* @Author: John
*/
import { GET, POST } from "./client";
@ -52,7 +52,7 @@ export function api_signUp() {
chainType: 2;
},
any
>({ url: "/api/account/signUp", requiresToken: false });
>({ url: "/api/account/signUp", requiresToken: false, catchErr: true });
}
// 获取钱包签名串

View File

@ -1,7 +1,7 @@
/*
* @LastEditors: John
* @Date: 2024-06-18 10:09:21
* @LastEditTime: 2024-06-21 14:47:26
* @LastEditTime: 2024-07-02 11:36:28
* @Author: John
*/
import { Client } from "@hyper-fetch/core";
@ -17,9 +17,11 @@ import i18next from "i18next";
function initClient({
requiresToken,
requiresAddress,
catchErr,
}: {
requiresToken: boolean;
requiresAddress: boolean;
catchErr: boolean;
}) {
return new Client({ url: import.meta.env.VITE_BASE_API_URL })
.onAuth(async (req) => {
@ -59,6 +61,7 @@ function initClient({
const resData: BASE_RESPONSE = res.data;
if (resData.code !== 200 && resData.code !== 0) {
if (resData.msg) Toast.show({ content: resData.msg, icon: "fail" });
if (catchErr) return res;
throw new Error(resData.msg || "client on response error");
}
return res;
@ -69,12 +72,14 @@ export const POST = <P = any, R = any, QueryParams = any>({
url,
requiresToken = true,
requiresAddress = true,
catchErr = false,
}: {
url: string;
requiresToken?: boolean;
requiresAddress?: boolean;
catchErr?: boolean;
}) => {
return initClient({ requiresToken, requiresAddress }).createRequest<
return initClient({ requiresToken, requiresAddress, catchErr }).createRequest<
BASE_RESPONSE<R>,
P,
any,
@ -89,12 +94,14 @@ export const GET = <P = any, R = any>({
url,
requiresToken = true,
requiresAddress = true,
catchErr = false,
}: {
url: string;
requiresToken?: boolean;
requiresAddress?: boolean;
catchErr?: boolean;
}) => {
return initClient({ requiresToken, requiresAddress }).createRequest<
return initClient({ requiresToken, requiresAddress, catchErr }).createRequest<
BASE_RESPONSE<R>,
any,
any,

View File

@ -22,7 +22,7 @@ export interface UserHomeData {
active: 0 | 1; // "0=非活跃 1=活跃用户"
}
export interface UserIncome {
coinId: number;
coinId: 1 | 2; // 1 USDT 2 ROMB
coinName: string;
collection: number;
createTime: string;

View File

@ -294,3 +294,21 @@
}
}
}
/* adm-pull-to-refresh */
.adm-pull-to-refresh {
.adm-pull-to-refresh-head-content {
/* 自动布局子元素 */
font-family: Space Grotesk;
font-size: 14px;
font-weight: 500;
line-height: normal;
letter-spacing: 0em;
font-variation-settings: "opsz" auto;
font-feature-settings: "kern" on;
color: #9e9e9e;
z-index: 0;
}
}

View File

@ -102,3 +102,14 @@ export function getLevelName(level: Level, active?: UserHomeData["active"]) {
break;
}
}
export function getUrlParameterByName(name: string, url?: string) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
let regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return "";
console.log("url params:", results);
return decodeURIComponent(results[2].replace(/\+/g, " "));
}

View File

@ -1,7 +1,7 @@
/*
* @LastEditors: John
* @Date: 2024-06-19 15:55:07
* @LastEditTime: 2024-06-26 15:18:22
* @LastEditTime: 2024-07-02 11:38:59
* @Author: John
*/
import { config } from "@/components/WalletProvider";
@ -24,7 +24,7 @@ import {
} from "@wagmi/core";
import Toast from "antd-mobile/es/components/toast";
import i18next from "i18next";
import { getUrlQueryParam } from ".";
import { getUrlParameterByName } from ".";
import { UrlQueryParamsKey } from "@/constants";
/**
@ -129,13 +129,13 @@ export async function signAndLogin(address?: `0x${string}`): Promise<void> {
loadingToast.close();
}
} else {
const inviteCode = getUrlQueryParam(UrlQueryParamsKey.INVITE_CODE);
const inviteCode = getUrlParameterByName(UrlQueryParamsKey.INVITE_CODE);
if (!inviteCode) {
Toast.show({ icon: "fail", content: i18next.t("无效的邀请链接") });
return loginOut();
}
// 注册
await api_signUp().send({
const { data } = await api_signUp().send({
data: {
account: address,
publicKey,
@ -143,7 +143,11 @@ export async function signAndLogin(address?: `0x${string}`): Promise<void> {
chainType: 2,
},
});
await signAndLogin(address);
if (data?.code === 0) {
await signAndLogin(address);
} else {
return loginOut();
}
reslove();
loadingToast.close();
}

3
src/vite-env.d.ts vendored
View File

@ -1,7 +1,7 @@
/*
* @LastEditors: John
* @Date: 2024-06-17 17:20:03
* @LastEditTime: 2024-06-21 13:50:16
* @LastEditTime: 2024-07-02 16:53:48
* @Author: John
*/
/// <reference types="vite/client" />
@ -12,6 +12,7 @@ interface ImportMetaEnv {
readonly VITE_PARTICIPATE_CHAIN_ID: number;
readonly VITE_NETWORK_USDT_ADDRESS: `0x${string}`;
readonly VITE_PURCHASED_CONTRACT_ADDRESS: `0x${string}`;
readonly VITE_RECEIVE_RAMB_CONTRACT_ADDRESS: `0x${string}`;
readonly VITE_CHECK_TRANSACTION_DETAILS_URL: string;
// 更多环境变量...
readonly MODE: "development" | "production" | "test";