From 9e771748a4991d89a37e6ae781a949bd5eb8113d Mon Sep 17 00:00:00 2001 From: deep-path <476044723@qq.com> Date: Wed, 20 Nov 2024 12:13:14 +0800 Subject: [PATCH] feat: add modal --- .../ModalWithClosePosition.tsx | 96 +++++++++++++++++++ components/HashResultModal/index.tsx | 21 ++++ screens/Trading/index.tsx | 82 ++-------------- services/transaction.ts | 76 +++++++++++++++ 4 files changed, 200 insertions(+), 75 deletions(-) create mode 100644 components/HashResultModal/ModalWithClosePosition.tsx create mode 100644 services/transaction.ts diff --git a/components/HashResultModal/ModalWithClosePosition.tsx b/components/HashResultModal/ModalWithClosePosition.tsx new file mode 100644 index 00000000..28f37452 --- /dev/null +++ b/components/HashResultModal/ModalWithClosePosition.tsx @@ -0,0 +1,96 @@ +import React, { useState, useEffect } from "react"; +import { NearIconMini } from "../../screens/MarginTrading/components/Icon"; +import { CloseIcon } from "../Icons/Icons"; + +interface FailureModalProps { + show: boolean; + onClose: () => void; + title?: string; + errorMessage?: string; + type?: "Long" | "Short"; +} + +const ModalWithFailure = ({ + show, + onClose, + title = "Close Position", + errorMessage = "Operation failed, please try again later", + type = "Long", +}: FailureModalProps) => { + const [isModalVisible, setIsModalVisible] = useState(false); + const [countdown, setCountdown] = useState(10); + const [progress, setProgress] = useState(100); + let countdownTimer; + + useEffect(() => { + if (show) { + setIsModalVisible(true); + startCountdown(); + } else { + setIsModalVisible(false); + clearTimeoutOrInterval(countdownTimer); + } + return () => clearTimeoutOrInterval(countdownTimer); + }, [show]); + + const clearTimeoutOrInterval = (timerId) => { + if (timerId) { + clearInterval(timerId); + countdownTimer = null; + } + }; + + const hideModal = () => { + setIsModalVisible(false); + onClose(); + clearTimeout(countdownTimer); + }; + + const startCountdown = () => { + setCountdown(10); + setProgress(100); + + const timerInterval = 1000; + countdownTimer = setInterval(() => { + setCountdown((prevCountdown) => { + if (prevCountdown <= 1) { + hideModal(); + clearTimeoutOrInterval(countdownTimer); + return 0; + } + return prevCountdown - 1; + }); + setProgress((prevProgress) => Math.max(prevProgress - 10, 0)); + }, timerInterval); + }; + + return ( +
+ {isModalVisible && ( +
+
+
+ +
+
+ + {title} +
Success
+
+
+
+
+
+
+ )} +
+ ); +}; + +export default ModalWithFailure; diff --git a/components/HashResultModal/index.tsx b/components/HashResultModal/index.tsx index caaf4c65..56e2bdea 100644 --- a/components/HashResultModal/index.tsx +++ b/components/HashResultModal/index.tsx @@ -2,6 +2,7 @@ import React from "react"; import { createRoot } from "react-dom/client"; import ModalWithCountdown from "./ModalWithCountdown"; import ModalWithFailure from "./ModalWithFailure"; +import ModalWithClosePosition from "./ModalWithClosePosition"; interface ShowPositionResultParams { title?: string; @@ -82,3 +83,23 @@ export const showPositionFailure = (params: { root?.render(); }; + +export const showPositionClose = (params: { title?: string; type?: "Long" | "Short" }) => { + if (!container) { + container = document.createElement("div"); + container.id = "position-result-container"; + document.body.appendChild(container); + root = createRoot(container); + } + const handleClose = () => { + if (root) { + root.unmount(); + } + if (container) { + container.remove(); + container = null; + root = null; + } + }; + root?.render(); +}; diff --git a/screens/Trading/index.tsx b/screens/Trading/index.tsx index fb1c3c56..6494b387 100644 --- a/screens/Trading/index.tsx +++ b/screens/Trading/index.tsx @@ -23,7 +23,12 @@ import { setCategoryAssets1, setCategoryAssets2 } from "../../redux/marginTradin import { useMarginAccount } from "../../hooks/useMarginAccount"; import { useAccountId, usePortfolioAssets } from "../../hooks/hooks"; import { useRouterQuery, getTransactionResult, parsedArgs } from "../../utils/txhashContract"; -import { showPositionResult, showPositionFailure } from "../../components/HashResultModal"; +import { + showPositionResult, + showPositionFailure, + showPositionClose, +} from "../../components/HashResultModal"; +import { handleTransactionResults } from "../../services/transaction"; init_env("dev"); @@ -138,82 +143,9 @@ const Trading = () => { }; useEffect(() => { - handleTransactions(); + handleTransactionResults(query?.transactionHashes, query?.errorMessage); }, [query?.transactionHashes, query?.errorMessage]); - const handleTransactions = async () => { - if (query?.transactionHashes) { - try { - const txhash = Array.isArray(query?.transactionHashes) - ? query?.transactionHashes - : (query?.transactionHashes as string).split(","); - const results = await Promise.all( - txhash.map(async (txHash: string) => { - const result: any = await getTransactionResult(txHash); - const hasStorageDeposit = result.transaction.actions.some( - (action: any) => action?.FunctionCall?.method_name === "margin_execute_with_pyth", - ); - return { txHash, result, hasStorageDeposit }; - }), - ); - results.forEach(({ txHash, result, hasStorageDeposit }: any) => { - if (hasStorageDeposit) { - // handleClaimTokenMobile({ level, count }); - console.log(result); - - const args = parsedArgs(result?.transaction?.actions?.[0]?.FunctionCall?.args || ""); - const { actions } = JSON.parse(args || ""); - - // const ft_on_transfer_id = result?.receipts?.findIndex((r: any) => - // r?.receipt?.Action?.actions?.some( - // (a: any) => a?.FunctionCall?.method_name === "margin_execute_with_pyth", - // ), - // ); - - // const ft_on_transfer_logs = - // result?.receipts_outcome?.[ft_on_transfer_id]?.outcome?.logs || ""; - // const ft_on_transfer_log = ft_on_transfer_logs?.[ft_on_transfer_logs?.length - 1]; - // const idx = ft_on_transfer_log?.indexOf("{"); - - // const parsed_ft_on_transfer_log = JSON.parse(ft_on_transfer_log.slice(idx) || ""); - const isLong = actions[0]?.OpenPosition?.token_p_id == "wrap.testnet"; - console.log(actions); - console.log(marginConfig); - showPositionResult({ - title: "Open Position", - type: isLong ? "Long" : "Short", - price: (isLong - ? Number(shrinkToken(actions[0]?.OpenPosition?.token_d_amount, 18)) / - Number(shrinkToken(actions[0]?.OpenPosition?.min_token_p_amount, 24)) - : Number(shrinkToken(actions[0]?.OpenPosition?.min_token_p_amount, 18)) / - Number(shrinkToken(actions[0]?.OpenPosition?.token_d_amount, 24)) - ).toString(), - transactionHashes: Array.isArray(router.query.transactionHashes) - ? router.query.transactionHashes[0] - : (router.query.transactionHashes as string), - positionSize: { - // optional, - amount: isLong - ? shrinkToken(actions[0]?.OpenPosition?.min_token_p_amount, 24, 6) - : shrinkToken(actions[0]?.OpenPosition?.token_d_amount, 24, 6), - symbol: "NEAR", - usdValue: "1000", - }, - }); - } - }); - } catch (error) { - console.error("Error processing transactions:", error); - } - } - if (query?.errorMessage) { - showPositionFailure({ - title: "Open Position", - errorMessage: decodeURIComponent(query.errorMessage as string), - }); - } - }; - // return ( diff --git a/services/transaction.ts b/services/transaction.ts new file mode 100644 index 00000000..76df7bbd --- /dev/null +++ b/services/transaction.ts @@ -0,0 +1,76 @@ +import { shrinkToken } from "../store"; +import { getTransactionResult, parsedArgs } from "../utils/txhashContract"; +import { + showPositionResult, + showPositionClose, + showPositionFailure, +} from "../components/HashResultModal"; + +interface TransactionResult { + txHash: string; + result: any; + hasStorageDeposit: boolean; +} + +export const handleTransactionResults = async ( + transactionHashes: string | string[] | undefined, + errorMessage?: string | string[], +) => { + if (transactionHashes) { + try { + const txhash = Array.isArray(transactionHashes) + ? transactionHashes + : transactionHashes.split(","); + + const results = await Promise.all( + txhash.map(async (txHash: string): Promise => { + const result: any = await getTransactionResult(txHash); + const hasStorageDeposit = result.transaction.actions.some( + (action: any) => action?.FunctionCall?.method_name === "margin_execute_with_pyth", + ); + return { txHash, result, hasStorageDeposit }; + }), + ); + + results.forEach(({ result, hasStorageDeposit }: TransactionResult) => { + if (hasStorageDeposit) { + const args = parsedArgs(result?.transaction?.actions?.[0]?.FunctionCall?.args || ""); + const { actions } = JSON.parse(args || ""); + const isLong = actions[0]?.OpenPosition?.token_p_id == "wrap.testnet"; + + if (actions[0]?.CloseMTPosition) { + showPositionClose({}); + } else { + showPositionResult({ + title: "Open Position", + type: isLong ? "Long" : "Short", + price: (isLong + ? Number(shrinkToken(actions[0]?.OpenPosition?.token_d_amount, 18)) / + Number(shrinkToken(actions[0]?.OpenPosition?.min_token_p_amount, 24)) + : Number(shrinkToken(actions[0]?.OpenPosition?.min_token_p_amount, 18)) / + Number(shrinkToken(actions[0]?.OpenPosition?.token_d_amount, 24)) + ).toString(), + transactionHashes: txhash[0], + positionSize: { + amount: isLong + ? shrinkToken(actions[0]?.OpenPosition?.min_token_p_amount, 24, 6) + : shrinkToken(actions[0]?.OpenPosition?.token_d_amount, 24, 6), + symbol: "NEAR", + usdValue: "1000", + }, + }); + } + } + }); + } catch (error) { + console.error("Error processing transactions:", error); + } + } + + if (errorMessage) { + showPositionFailure({ + title: "Open Position", + errorMessage: decodeURIComponent(errorMessage as string), + }); + } +};