feat:

This commit is contained in:
john 2024-06-28 18:00:45 +08:00
parent c1a8cfd6d6
commit a58b225544
15 changed files with 582 additions and 422 deletions

View File

@ -1,7 +1,7 @@
/* /*
* @LastEditors: John * @LastEditors: John
* @Date: 2024-06-17 18:25:10 * @Date: 2024-06-17 18:25:10
* @LastEditTime: 2024-06-19 16:14:06 * @LastEditTime: 2024-06-28 17:46:23
* @Author: John * @Author: John
*/ */
export default { export default {
@ -11,7 +11,7 @@ export default {
rootValue: 37.5, rootValue: 37.5,
propList: ["*"], propList: ["*"],
exclude: (e) => { exclude: (e) => {
if (/.*-m\.css$/.test(e) || /.module\.css$/.test(e)) { if (/.*-m\.css$/.test(e) || /.*-m.module\.css$/.test(e)) {
// console.log(e); // console.log(e);
return false; return false;
} }

View File

@ -1,7 +1,7 @@
/* /*
* @LastEditors: John * @LastEditors: John
* @Date: 2024-06-17 17:20:03 * @Date: 2024-06-17 17:20:03
* @LastEditTime: 2024-06-26 16:00:00 * @LastEditTime: 2024-06-28 17:44:02
* @Author: John * @Author: John
*/ */
import { Route, Routes } from "react-router-dom"; import { Route, Routes } from "react-router-dom";
@ -16,7 +16,7 @@ import InvitationList from "./pages/InvitationList";
import { useEffect } from "react"; import { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import useUserStore from "./store/User"; import useUserStore from "./store/User";
import { getUrlQueryParam } from "./utils"; import { getUrlQueryParam, isMobile } from "./utils";
import { UrlQueryParamsKey } from "./constants"; import { UrlQueryParamsKey } from "./constants";
import { signAndLogin } from "./utils/wallet"; import { signAndLogin } from "./utils/wallet";
import { useAccount } from "wagmi"; import { useAccount } from "wagmi";
@ -45,12 +45,30 @@ function App() {
return ( return (
<> <>
<RouterLogProvider> <RouterLogProvider>
<Header /> {isMobile ? (
<Routes> <>
<Route path="/" element={<Home />} /> <Header.Mobile />
<Route path="/mint" element={<Mint />} /> <Routes>
<Route path="/invitationlist" element={<InvitationList />}></Route> <>
</Routes> <Route path="/" element={<Home.Mobile />} />
<Route path="/mint" element={<Mint />} />
<Route
path="/invitationlist"
element={<InvitationList />}
></Route>
</>
</Routes>
</>
) : (
<>
<Header.Desktop />
<Routes>
<>
<Route path="/" element={<Home.Desktop />} />
</>
</Routes>
</>
)}
</RouterLogProvider> </RouterLogProvider>
</> </>
); );

View File

@ -1,7 +1,7 @@
/* /*
* @LastEditors: John * @LastEditors: John
* @Date: 2024-06-18 15:16:31 * @Date: 2024-06-18 15:16:31
* @LastEditTime: 2024-06-27 15:29:00 * @LastEditTime: 2024-06-28 14:05:29
* @Author: John * @Author: John
*/ */
import Picker, { import Picker, {
@ -11,7 +11,7 @@ import Picker, {
import { useContext, useEffect, useMemo, useState } from "react"; import { useContext, useEffect, useMemo, useState } from "react";
import IconFont from "./iconfont"; import IconFont from "./iconfont";
import logo from "@/assets/logo.svg"; import logo from "@/assets/logo.svg";
import classes from "./Header.module.css"; import classes from "./Header-m.module.css";
import { useLocation, useNavigate, useNavigation } from "react-router-dom"; import { useLocation, useNavigate, useNavigation } from "react-router-dom";
import { RouterLogContext } from "@/context/RouterContext"; import { RouterLogContext } from "@/context/RouterContext";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -27,7 +27,7 @@ const langColums: Action[] = [
{ text: "日本語", key: Lang.jp }, { text: "日本語", key: Lang.jp },
{ text: "DEUTSCH", key: Lang.de }, { text: "DEUTSCH", key: Lang.de },
]; ];
export default function () { function Mobile() {
const { UpdateLang } = useUserStore(); const { UpdateLang } = useUserStore();
const route = useContext(RouterLogContext); const route = useContext(RouterLogContext);
@ -79,3 +79,12 @@ export default function () {
</> </>
); );
} }
function Desktop() {
return <></>;
}
export default {
Mobile,
Desktop,
};

View File

@ -1,10 +1,10 @@
/* /*
* @LastEditors: John * @LastEditors: John
* @Date: 2024-06-19 10:49:42 * @Date: 2024-06-19 10:49:42
* @LastEditTime: 2024-06-19 10:52:32 * @LastEditTime: 2024-06-28 14:05:45
* @Author: John * @Author: John
*/ */
import classes from "./RecordsItem.module.css"; import classes from "./RecordsItem-m.module.css";
export default function RecordsItem({ export default function RecordsItem({
itemList, itemList,
}: { }: {

View File

@ -0,0 +1,103 @@
/*
* @LastEditors: John
* @Date: 2024-06-28 14:09:55
* @LastEditTime: 2024-06-28 14:14:29
* @Author: John
*/
import useUserStore from "@/store/User";
import { useWeb3Modal } from "@web3modal/wagmi/react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAccount } from "wagmi";
import { useNavigate } from "react-router-dom";
import { Dialog } from "antd-mobile";
import {
api_get_homepage_user_data,
api_query_user_invitation_code,
} from "@/server/api";
import { UserHomeData } from "@/server/module";
import { UrlQueryParamsKey } from "@/constants";
import usePollingCheckBuyStatus from "@/hook/usePollingCheckBuyStatus";
import { ToastHandler } from "antd-mobile/es/components/toast";
import { PullStatus } from "antd-mobile/es/components/pull-to-refresh";
export default function () {
const { Token, UpdateToken } = useUserStore();
const { open } = useWeb3Modal();
const { t } = useTranslation();
const { address } = useAccount();
const [tabIndex, setTabIndex] = useState(0);
const navigate = useNavigate();
const [userData, setUserData] = useState<UserHomeData>();
const [inviteCode, setInviteCode] = useState("");
const userInviteLink = useMemo(
() =>
`${location.origin}/#/?${UrlQueryParamsKey.INVITE_CODE}=${inviteCode}`,
[inviteCode]
);
const receiveLoadingToast = useRef<ToastHandler>();
const {
transcationStatus,
startPollingCheckBuyStatus,
stopPollingCheckBuyStatus,
} = usePollingCheckBuyStatus("NORMAL");
const statusRecord: Record<PullStatus, string> = {
pulling: "Pull down to refresh",
canRelease: "Release to refresh immediately",
refreshing: "Loading...",
complete: "Refresh complete",
};
useEffect(() => {
if (Token) {
getHomeData();
getInviteCode();
}
return () => {};
}, [Token]);
useEffect(() => {
if (transcationStatus == "success") {
receiveLoadingToast.current?.close();
stopPollingCheckBuyStatus();
Dialog.alert({
content: `${t("领取成功,前往钱包查看")}`,
confirmText: "OK",
});
}
return () => {};
}, [transcationStatus]);
async function getHomeData() {
const { data } = await api_get_homepage_user_data().send({});
setUserData(data?.data);
}
async function getInviteCode() {
const { data } = await api_query_user_invitation_code().send({});
setInviteCode(data?.data.invitationCode || "");
}
useEffect(() => {
console.log("user token:", Token);
return () => {};
}, [Token]);
return {
getHomeData,
statusRecord,
Token,
address,
setUserData,
userData,
tabIndex,
t,
setTabIndex,
navigate,
userInviteLink,
};
}

View File

@ -0,0 +1,41 @@
import usePagination from "@/hook/usePagination";
import { api_recommended_list } from "@/server/api";
import { RecommendedListItem } from "@/server/module";
import { Empty, InfiniteScroll } from "antd-mobile";
import DataTable, { TableColumn } from "react-data-table-component";
import { useTranslation } from "react-i18next";
export default function () {
const { t } = useTranslation();
const columns: TableColumn<RecommendedListItem>[] = [
{
name: t("地址"),
selector: (row) => row.walletAddress,
grow: 9,
},
{
name: "Node",
selector: (row) => row.nodeNumber,
grow: 1,
// @ts-ignore
right: "true",
},
];
const { list, hasMore, loadMore } = usePagination<RecommendedListItem>({
service({ pageNum, pageSize }) {
return new Promise(async (reslove) => {
const { data } = await api_recommended_list().send({
queryParams: { pageNum, pageSize },
});
reslove(data?.data.records || []);
});
},
});
return {
columns,
list,
loadMore,
hasMore,
};
}

View File

@ -0,0 +1,118 @@
import { cn, filterAddressBeforeZero, filterAmountBeforeZero } from "@/utils";
import classes from "./Mint-m.module.css";
import nft_bg from "@/assets/nft_bg.svg";
import Button from "antd-mobile/es/components/button";
import Space from "antd-mobile/es/components/space";
import { useTranslation } from "react-i18next";
import { useEffect, useMemo, useRef, useState } from "react";
import {
api_get_homepage_user_data,
api_get_nft_configuration_data,
api_node_order,
api_users_cancel_orders,
} from "@/server/api";
import {
NftConfigurationData,
NftOrder,
NodeOrder,
UserHomeData,
} from "@/server/module";
import {
authorizedU,
getApproveUsdt,
getBalance,
payByContract,
} from "@/contract/utils";
import { Dialog, Modal, Stepper, Toast } from "antd-mobile";
import useUserStore from "@/store/User";
import usePollingCheckBuyStatus from "@/hook/usePollingCheckBuyStatus";
import { ToastHandler } from "antd-mobile/es/components/toast";
import { BaseError } from "wagmi";
import { useNavigate } from "react-router-dom";
import { toWei } from "web3-utils";
export default function () {
const { t } = useTranslation();
const { Token } = useUserStore();
const [nodeConfig, setNodeConfig] = useState<UserHomeData>();
const [approveUsdt, setApproveUsdt] = useState<bigint>(0n);
const [balance, setBalance] = useState<bigint>(0n);
const orderInfo = useRef<NodeOrder>();
const navigate = useNavigate();
const [num, setNum] = useState(1);
const buyLoadingToast = useRef<ToastHandler>();
const approveLoadingToast = useRef<ToastHandler>();
const {
transcationStatus,
startPollingCheckBuyStatus,
stopPollingCheckBuyStatus,
} = usePollingCheckBuyStatus("NORMAL");
const costNum = useMemo(
() =>
BigInt(
toWei(`${parseFloat(`${nodeConfig?.nodePrice || "0"}`) * num}`, "ether")
),
[nodeConfig?.nodePrice, num]
);
useEffect(() => {
updateNodeConfig();
return () => {};
}, []);
useEffect(() => {
(async () => {
Toast.show({ icon: "loading", content: t("正在获取已授权金额") });
setBalance(await getBalance());
setApproveUsdt(await getApproveUsdt());
Toast.clear();
})();
return () => {};
}, [Token]);
async function updateNodeConfig() {
const { data } = await api_get_homepage_user_data().send({});
setNodeConfig(data?.data);
}
useEffect(() => {
if (transcationStatus == "success") {
buyLoadingToast.current?.close();
stopPollingCheckBuyStatus();
Dialog.alert({
content: `${t(
"Buy successful. Please return to the homepage to view."
)}`,
confirmText: "OK",
onConfirm() {
navigate("/");
},
});
}
return () => {};
}, [transcationStatus]);
useEffect(() => {
return () => {};
}, []);
return {
nodeConfig,
num,
setNum,
approveUsdt,
t,
balance,
approveLoadingToast,
costNum,
setApproveUsdt,
buyLoadingToast,
orderInfo,
updateNodeConfig,
startPollingCheckBuyStatus,
};
}

View File

@ -1,314 +1,287 @@
import classes from "./Home.module.css"; /*
import useUserStore from "@/store/User"; * @LastEditors: John
* @Date: 2024-06-26 15:04:10
* @LastEditTime: 2024-06-28 14:36:37
* @Author: John
*/
import classes from "./Home-m.module.css";
import { cn, copyText, getLevelName, shortenString } from "@/utils"; import { cn, copyText, getLevelName, shortenString } from "@/utils";
import { useWeb3Modal } from "@web3modal/wagmi/react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import logo from "@/assets/logo.svg"; import logo from "@/assets/logo.svg";
import nftBg from "@/assets/nft_bg.svg"; import nftBg from "@/assets/nft_bg.svg";
import usdtBg from "@/assets/usdt_bg.svg";
import IconFont from "@/components/iconfont"; import IconFont from "@/components/iconfont";
import { BaseError, useAccount } from "wagmi"; import { PullToRefresh } from "antd-mobile";
import { config } from "@/components/WalletProvider";
import { createSearchParams, useNavigate } from "react-router-dom";
import { Button, Dialog, Empty, PullToRefresh, Toast } from "antd-mobile";
import { loginOut } from "@/utils/wallet"; import { loginOut } from "@/utils/wallet";
import { import useHome from "./Feature/useHome";
api_claim_income, import useMint from "./Feature/useMint";
api_get_homepage_user_data, import useInvitationList from "./Feature/useInvitationList";
api_query_user_invitation_code, function Mobile() {
} from "@/server/api";
import { UserHomeData } from "@/server/module";
import { UrlQueryParamsKey } from "@/constants";
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();
const { t } = useTranslation();
const { address } = useAccount();
const [tabIndex, setTabIndex] = useState(0);
const navigate = useNavigate();
const [userData, setUserData] = useState<UserHomeData>();
const [inviteCode, setInviteCode] = useState("");
const userInviteLink = useMemo(
() =>
`${location.origin}/#/?${UrlQueryParamsKey.INVITE_CODE}=${inviteCode}`,
[inviteCode]
);
const receiveLoadingToast = useRef<ToastHandler>();
const { const {
transcationStatus, getHomeData,
startPollingCheckBuyStatus, statusRecord,
stopPollingCheckBuyStatus, Token,
} = usePollingCheckBuyStatus("NORMAL"); address,
setUserData,
const statusRecord: Record<PullStatus, string> = { userData,
pulling: "Pull down to refresh", tabIndex,
canRelease: "Release to refresh immediately", t,
refreshing: "Loading...", setTabIndex,
complete: "Refresh complete", navigate,
}; userInviteLink,
} = useHome();
useEffect(() => {
if (Token) {
getHomeData();
getInviteCode();
}
return () => {};
}, [Token]);
useEffect(() => {
if (transcationStatus == "success") {
receiveLoadingToast.current?.close();
stopPollingCheckBuyStatus();
Dialog.alert({
content: `${t("领取成功,前往钱包查看")}`,
confirmText: "OK",
});
}
return () => {};
}, [transcationStatus]);
async function getHomeData() {
const { data } = await api_get_homepage_user_data().send({});
setUserData(data?.data);
}
async function getInviteCode() {
const { data } = await api_query_user_invitation_code().send({});
setInviteCode(data?.data.invitationCode || "");
}
useEffect(() => {
console.log("user token:", Token);
return () => {};
}, [Token]);
return ( return (
<> <PullToRefresh
<PullToRefresh onRefresh={async () => {
onRefresh={async () => { await getHomeData();
await getHomeData(); }}
}} renderText={(status) => {
renderText={(status) => { return <div>{statusRecord[status]}</div>;
return <div>{statusRecord[status]}</div>; }}
}} disabled={!Token}
disabled={!Token} >
> <div className={cn(classes.Home, classes.container)}>
<div className={cn(classes.Home, classes.container)}> <div className={classes.userinfo}>
<div className={classes.userinfo}> <div className={classes.userinfo_top}>
<div className={classes.userinfo_top}> <img className={classes.userinfo_top_left} src={logo} alt="" />
<img className={classes.userinfo_top_left} src={logo} alt="" />
{address ? ( {address ? (
<div className={classes.userinfo_top_right}> <div className={classes.userinfo_top_right}>
<div className={classes.userinfo_top_right_wallet}> <div className={classes.userinfo_top_right_wallet}>
<span>{shortenString(address, 6, 4)}</span> <span>{shortenString(address, 6, 4)}</span>
<IconFont <IconFont
onClick={async () => { onClick={async () => {
loginOut(); loginOut();
setUserData(undefined); setUserData(undefined);
}}
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}>
<span>{getLevelName(userData.level)}</span>
</div>
</>
)}
</div>
</div>
) : (
<>
<div
className={classes.userinfo_top_right_connect}
onClick={() => {
open();
}} }}
> name="tuichu"
<span>{t("链接钱包")}</span> className={classes.userinfo_top_right_wallet_disconnect}
</div> color={"#fff"}
</> />
)} </div>
</div> <div className={classes.userinfo_top_right_btns}>
<ul className={classes.userinfo_data}> {userData && (
<li>
<span className={classes.userinfo_data_num}>
{userData?.directPushNode || 0}
</span>
<span className={classes.userinfo_data_des}>
{t("Direct Node")}
</span>
</li>
<li>
<span className={classes.userinfo_data_num}>
{userData?.teamNode || 0}
</span>
<span className={classes.userinfo_data_des}>
{t("Team Node")}
</span>
</li>
<li>
<span className={classes.userinfo_data_num}>
{userData?.revenueUsdt || 0}
<p>USDT</p>
</span>
<span className={classes.userinfo_data_des}>
{t("Push income")}
</span>
</li>
</ul>
</div>
<div className={classes.nftToken}>
<ul className={classes.nftToken_tab}>
<li
className={tabIndex == 0 ? classes.nftToken_tab_active : ""}
onClick={() => setTabIndex(0)}
>
NODE
</li>
</ul>
<div className={classes.nftToken_content}>
{tabIndex == 0 && (
<>
{address ? (
<> <>
{userData?.nodeNumber && userData?.nodeNumber > 0 ? ( <div className={classes.userinfo_top_right_btns_item}>
<div className={classes.nftToken_content_nft}> <span>{getLevelName(userData.level)}</span>
<div className={classes.nftToken_content_nft_top}> </div>
<span>You own {userData.nodeNumber} nodes</span> </>
<span )}
onClick={() => { </div>
navigate("/mint"); </div>
}} ) : (
> <>
{t("Buy Node")} <div
<IconFont className={classes.userinfo_top_right_connect}
name="chevronsrightshuangyoujiantou" onClick={() => {
color={"#2DFCFC"} open();
/> }}
</span> >
</div> <span>{t("链接钱包")}</span>
<img </div>
className={classes.nftToken_content_nft_img} </>
src={nftBg} )}
alt="" </div>
/> <ul className={classes.userinfo_data}>
<span className={classes.nftToken_content_nft_des}> <li>
{t("Start mining after node subscription ends.")} <span className={classes.userinfo_data_num}>
</span> {userData?.directPushNode || 0}
</div> </span>
) : ( <span className={classes.userinfo_data_des}>
<div className={classes.nftToken_content_nft_mint}> {t("Direct Node")}
<div </span>
className={classes.nftToken_content_nft_mint_btn} </li>
<li>
<span className={classes.userinfo_data_num}>
{userData?.teamNode || 0}
</span>
<span className={classes.userinfo_data_des}>
{t("Team Node")}
</span>
</li>
<li>
<span className={classes.userinfo_data_num}>
{userData?.revenueUsdt || 0}
<p>USDT</p>
</span>
<span className={classes.userinfo_data_des}>
{t("Push income")}
</span>
</li>
</ul>
</div>
<div className={classes.nftToken}>
<ul className={classes.nftToken_tab}>
<li
className={tabIndex == 0 ? classes.nftToken_tab_active : ""}
onClick={() => setTabIndex(0)}
>
NODE
</li>
</ul>
<div className={classes.nftToken_content}>
{tabIndex == 0 && (
<>
{address ? (
<>
{userData?.nodeNumber && userData?.nodeNumber > 0 ? (
<div className={classes.nftToken_content_nft}>
<div className={classes.nftToken_content_nft_top}>
<span>You own {userData.nodeNumber} nodes</span>
<span
onClick={() => { onClick={() => {
navigate("/mint"); navigate("/mint");
}} }}
> >
<span>{t("Buy Node")}</span> {t("Buy Node")}
<IconFont <IconFont
className={
classes.nftToken_content_nft_mint_btn_icon
}
name="chevronsrightshuangyoujiantou" name="chevronsrightshuangyoujiantou"
color={"#101010"} color={"#2DFCFC"}
/> />
</div> </span>
<span>{t("Buy Edge AI Node.")}</span>
</div> </div>
)} <img
</> className={classes.nftToken_content_nft_img}
) : ( src={nftBg}
<> alt=""
<div className={classes.nftToken_content_userDisconnect}> />
<span> <span className={classes.nftToken_content_nft_des}>
{t( {t("Start mining after node subscription ends.")}
"The wallet is not linked and cannot show you the Node."
)}
</span> </span>
</div> </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={"#2DFCFC"}
/>
</span>
)}
</div>
<div className={classes.invite_content}>
<span>{t("邀请链接")}</span>
<div className={classes.invite_content_link}>
{address ? (
<>
{userData?.nodeNumber && userData?.nodeNumber > 0 ? (
<>
<span>{shortenString(userInviteLink, 15, 15)}</span>
<IconFont
onClick={() => {
copyText(userInviteLink);
}}
className={classes.invite_content_icon}
name="fuzhi"
color={"#fff"}
/>{" "}
</>
) : ( ) : (
<> <div className={classes.nftToken_content_nft_mint}>
<span> <div
{t("Buy Edge AI Node to get invitation link")} className={classes.nftToken_content_nft_mint_btn}
</span> onClick={() => {
</> navigate("/mint");
}}
>
<span>{t("Buy Node")}</span>
<IconFont
className={
classes.nftToken_content_nft_mint_btn_icon
}
name="chevronsrightshuangyoujiantou"
color={"#101010"}
/>
</div>
<span>{t("Buy Edge AI Node.")}</span>
</div>
)} )}
</> </>
) : ( ) : (
<> <>
<span>{t("Link wallet to get invitation link")}</span> <div className={classes.nftToken_content_userDisconnect}>
<span>
{t(
"The wallet is not linked and cannot show you the Node."
)}
</span>
</div>
</> </>
)} )}
</div> </>
)}
<span>
{t(
"Invite your friends to become YOTTA nodes and you will get a 20% rebate."
)}
</span>
</div>
</div> </div>
</div> </div>
</PullToRefresh>
</> <div className={classes.invite}>
<div className={classes.invite_top}>
<span>{t("邀请")}</span>
{address && (
<span
onClick={() => {
navigate("/invitationlist");
}}
>
{t("邀请列表")}{" "}
<IconFont
name="chevronsrightshuangyoujiantou"
color={"#2DFCFC"}
/>
</span>
)}
</div>
<div className={classes.invite_content}>
<span>{t("邀请链接")}</span>
<div className={classes.invite_content_link}>
{address ? (
<>
{userData?.nodeNumber && userData?.nodeNumber > 0 ? (
<>
<span>{shortenString(userInviteLink, 15, 15)}</span>
<IconFont
onClick={() => {
copyText(userInviteLink);
}}
className={classes.invite_content_icon}
name="fuzhi"
color={"#fff"}
/>{" "}
</>
) : (
<>
<span>
{t("Buy Edge AI Node to get invitation link")}
</span>
</>
)}
</>
) : (
<>
<span>{t("Link wallet to get invitation link")}</span>
</>
)}
</div>
<span>
{t(
"Invite your friends to become YOTTA nodes and you will get a 20% rebate."
)}
</span>
</div>
</div>
</div>
</PullToRefresh>
); );
} }
function Desktop() {
const {
getHomeData,
statusRecord,
Token,
address,
setUserData,
userData,
tabIndex,
t,
setTabIndex,
navigate,
userInviteLink,
} = useHome();
const {
nodeConfig,
num,
setNum,
approveUsdt,
balance,
approveLoadingToast,
costNum,
setApproveUsdt,
buyLoadingToast,
orderInfo,
updateNodeConfig,
startPollingCheckBuyStatus,
} = useMint();
const { columns, list, loadMore, hasMore } = useInvitationList();
return <></>;
}
export default {
Mobile,
Desktop,
};

View File

@ -4,40 +4,12 @@
* @LastEditTime: 2024-06-27 15:33:01 * @LastEditTime: 2024-06-27 15:33:01
* @Author: John * @Author: John
*/ */
import usePagination from "@/hook/usePagination";
import { api_recommended_list } from "@/server/api";
import { RecommendedListItem } from "@/server/module";
import { Empty, InfiniteScroll } from "antd-mobile"; import { Empty, InfiniteScroll } from "antd-mobile";
import DataTable, { TableColumn } from "react-data-table-component"; import DataTable from "react-data-table-component";
import { useTranslation } from "react-i18next"; import useInvitationList from "./Feature/useInvitationList";
export default function () { export default function () {
const { t } = useTranslation(); const { columns, list, loadMore, hasMore } = useInvitationList();
const columns: TableColumn<RecommendedListItem>[] = [
{
name: t("地址"),
selector: (row) => row.walletAddress,
grow: 9,
},
{
name: "Node",
selector: (row) => row.nodeNumber,
grow: 1,
// @ts-ignore
right: "true",
},
];
const { list, hasMore, loadMore } = usePagination<RecommendedListItem>({
service({ pageNum, pageSize }) {
return new Promise(async (reslove) => {
const { data } = await api_recommended_list().send({
queryParams: { pageNum, pageSize },
});
reslove(data?.data.records || []);
});
},
});
return ( return (
<> <>

View File

@ -1,110 +1,36 @@
/* /*
* @LastEditors: John * @LastEditors: John
* @Date: 2024-06-18 15:28:03 * @Date: 2024-06-18 15:28:03
* @LastEditTime: 2024-06-27 17:17:09 * @LastEditTime: 2024-06-28 14:33:02
* @Author: John * @Author: John
*/ */
import { cn, filterAddressBeforeZero, filterAmountBeforeZero } from "@/utils"; import { cn, filterAddressBeforeZero, filterAmountBeforeZero } from "@/utils";
import classes from "./Mint.module.css"; import classes from "./Mint-m.module.css";
import nft_bg from "@/assets/nft_bg.svg"; import nft_bg from "@/assets/nft_bg.svg";
import Button from "antd-mobile/es/components/button"; import Button from "antd-mobile/es/components/button";
import Space from "antd-mobile/es/components/space"; import Space from "antd-mobile/es/components/space";
import { useTranslation } from "react-i18next"; import { api_node_order } from "@/server/api";
import { useEffect, useMemo, useRef, useState } from "react"; import { authorizedU, getApproveUsdt, payByContract } from "@/contract/utils";
import { import { Stepper, Toast } from "antd-mobile";
api_get_homepage_user_data,
api_get_nft_configuration_data,
api_node_order,
api_users_cancel_orders,
} from "@/server/api";
import {
NftConfigurationData,
NftOrder,
NodeOrder,
UserHomeData,
} from "@/server/module";
import {
authorizedU,
getApproveUsdt,
getBalance,
payByContract,
} from "@/contract/utils";
import { Dialog, Modal, Stepper, Toast } from "antd-mobile";
import useUserStore from "@/store/User";
import usePollingCheckBuyStatus from "@/hook/usePollingCheckBuyStatus";
import { ToastHandler } from "antd-mobile/es/components/toast";
import { BaseError } from "wagmi"; import { BaseError } from "wagmi";
import { useNavigate } from "react-router-dom";
import { toWei } from "web3-utils"; import { toWei } from "web3-utils";
import useMint from "./Feature/useMint";
export default function () { export default function () {
const { t } = useTranslation();
const { Token } = useUserStore();
const [nodeConfig, setNodeConfig] = useState<UserHomeData>();
const [approveUsdt, setApproveUsdt] = useState<bigint>(0n);
const [balance, setBalance] = useState<bigint>(0n);
const orderInfo = useRef<NodeOrder>();
const navigate = useNavigate();
const [num, setNum] = useState(1);
const buyLoadingToast = useRef<ToastHandler>();
const approveLoadingToast = useRef<ToastHandler>();
const { const {
transcationStatus, nodeConfig,
num,
setNum,
approveUsdt,
t,
balance,
approveLoadingToast,
costNum,
setApproveUsdt,
buyLoadingToast,
orderInfo,
updateNodeConfig,
startPollingCheckBuyStatus, startPollingCheckBuyStatus,
stopPollingCheckBuyStatus, } = useMint();
} = usePollingCheckBuyStatus("NORMAL");
const costNum = useMemo(
() =>
BigInt(
toWei(`${parseFloat(`${nodeConfig?.nodePrice || "0"}`) * num}`, "ether")
),
[nodeConfig?.nodePrice, num]
);
useEffect(() => {
updateNodeConfig();
return () => {};
}, []);
useEffect(() => {
(async () => {
Toast.show({ icon: "loading", content: t("正在获取已授权金额") });
setBalance(await getBalance());
setApproveUsdt(await getApproveUsdt());
Toast.clear();
})();
return () => {};
}, [Token]);
async function updateNodeConfig() {
const { data } = await api_get_homepage_user_data().send({});
setNodeConfig(data?.data);
}
useEffect(() => {
if (transcationStatus == "success") {
buyLoadingToast.current?.close();
stopPollingCheckBuyStatus();
Dialog.alert({
content: `${t(
"Buy successful. Please return to the homepage to view."
)}`,
confirmText: "OK",
onConfirm() {
navigate("/");
},
});
}
return () => {};
}, [transcationStatus]);
useEffect(() => {
return () => {};
}, []);
return ( return (
<> <>
<div className={cn(classes.Mint, classes.container)}> <div className={cn(classes.Mint, classes.container)}>

View File

@ -1,7 +1,7 @@
/* /*
* @LastEditors: John * @LastEditors: John
* @Date: 2024-06-17 18:19:27 * @Date: 2024-06-17 18:19:27
* @LastEditTime: 2024-06-27 11:40:59 * @LastEditTime: 2024-06-28 15:32:57
* @Author: John * @Author: John
*/ */
import { type ClassValue, clsx } from "clsx"; import { type ClassValue, clsx } from "clsx";