mirror of
https://github.com/Qortal/q-trade.git
synced 2025-06-15 19:01:21 +00:00
added stuck sell order table
This commit is contained in:
parent
e0ab3846d7
commit
733c49e33c
10
package-lock.json
generated
10
package-lock.json
generated
@ -28,6 +28,7 @@
|
||||
"react-qr-code": "^2.0.15",
|
||||
"react-router-dom": "^6.23.0",
|
||||
"react-toastify": "^10.0.5",
|
||||
"react-virtuoso": "^4.12.7",
|
||||
"sass": "^1.76.0",
|
||||
"short-unique-id": "^5.2.0",
|
||||
"socket.io-client": "^4.7.5"
|
||||
@ -6846,6 +6847,15 @@
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-virtuoso": {
|
||||
"version": "4.12.7",
|
||||
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.12.7.tgz",
|
||||
"integrity": "sha512-njJp764he6Fi1p89PUW0k2kbyWu9w/y+MwdxmwK2kvdwwzVDbz2c2wMj5xdSruBFVgFTsI7Z85hxZR7aSHBrbQ==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16 || >=17 || >= 18 || >= 19",
|
||||
"react-dom": ">=16 || >=17 || >= 18 || >=19"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"react-qr-code": "^2.0.15",
|
||||
"react-router-dom": "^6.23.0",
|
||||
"react-toastify": "^10.0.5",
|
||||
"react-virtuoso": "^4.12.7",
|
||||
"sass": "^1.76.0",
|
||||
"short-unique-id": "^5.2.0",
|
||||
"socket.io-client": "^4.7.5"
|
||||
|
@ -56,12 +56,15 @@ import {
|
||||
|
||||
export const baseLocalHost = window.location.host;
|
||||
// export const baseLocalHost = "devnet-nodes.qortal.link:11111";
|
||||
// export const baseLocalHost = "127.0.0.1:12391";
|
||||
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
||||
import moment from "moment";
|
||||
import { RequestQueueWithPromise } from "qapp-core";
|
||||
import { useUpdateFee } from "../../hooks/useUpdateFee";
|
||||
import { useSetAtom } from "jotai/react";
|
||||
import { stuckTradesAtom } from "../../global/state";
|
||||
|
||||
const copyToClipboard = (text: string) => {
|
||||
navigator.clipboard.writeText(text);
|
||||
@ -97,6 +100,7 @@ export const TradeOffers: React.FC<any> = ({
|
||||
const [offers, setOffers] = useState<any[]>([]);
|
||||
const [signedUnlockingFees, setSignedUnlockingFees] = useState(null);
|
||||
const [qortalNames, setQortalNames] = useState({});
|
||||
const setStuckTrades = useSetAtom(stuckTradesAtom)
|
||||
const {
|
||||
fetchOngoingTransactions,
|
||||
onGoingTrades,
|
||||
@ -448,6 +452,10 @@ export const TradeOffers: React.FC<any> = ({
|
||||
return offeringTrade.tradePresenceExpiry > Date.now();
|
||||
};
|
||||
|
||||
const filterForStuckDrades = (offeringTrade: any) => {
|
||||
return (!offeringTrade?.tradePresenceExpiry || offeringTrade.tradePresenceExpiry < Date.now());
|
||||
};
|
||||
|
||||
const startOfferPresenceMapping = async () => {
|
||||
if (tradePresenceTxns.current) {
|
||||
for (const tradePresence of tradePresenceTxns.current) {
|
||||
@ -467,6 +475,11 @@ export const TradeOffers: React.FC<any> = ({
|
||||
offeringTrades.current?.filter((offeringTrade) =>
|
||||
filterOffersUsingTradePresence(offeringTrade)
|
||||
) || [];
|
||||
|
||||
const stuckTrades = offeringTrades.current?.filter((offeringTrade) =>
|
||||
filterForStuckDrades(offeringTrade)
|
||||
) || [];
|
||||
setStuckTrades(stuckTrades?.sort((a, b) => b.timestamp - a.timestamp))
|
||||
let tradesPresenceCleaned: any[] = filteredOffers;
|
||||
|
||||
blockedTradesList.current.forEach((item: any) => {
|
||||
@ -549,10 +562,10 @@ export const TradeOffers: React.FC<any> = ({
|
||||
socketRef.current.onmessage = (e) => {
|
||||
offeringTrades.current = [
|
||||
...offeringTrades.current?.filter(
|
||||
(coin) => coin?.foreignBlockchain === selectedCoin
|
||||
(coin) => coin?.foreignBlockchain === selectedCoin && coin?.mode === 'OFFERING'
|
||||
),
|
||||
...JSON.parse(e.data)?.filter(
|
||||
(coin) => coin?.foreignBlockchain === selectedCoin
|
||||
(coin) => coin?.foreignBlockchain === selectedCoin && coin?.mode === 'OFFERING'
|
||||
),
|
||||
];
|
||||
restarted = false;
|
||||
|
@ -69,6 +69,8 @@ import UnsignedFees from "../sell/UnsignedFees";
|
||||
import { FeeManager } from "../sell/FeeManager";
|
||||
import { Info } from "../sell/Info";
|
||||
import { Settings } from "../sell/Settings";
|
||||
import { useSetAtom } from "jotai/react";
|
||||
import { stuckTradesAtom } from "../../global/state";
|
||||
|
||||
const checkIfLocal = async () => {
|
||||
try {
|
||||
@ -171,6 +173,8 @@ export const Header = ({
|
||||
const [amount, setAmount] = useState<string>("");
|
||||
const [coinAddresses, setCoinAddresses] = useState({});
|
||||
const { isUsingGateway } = useContext(gameContext);
|
||||
const setStuckTrades = useSetAtom(stuckTradesAtom)
|
||||
|
||||
|
||||
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
setChecked(false);
|
||||
@ -492,6 +496,7 @@ export const Header = ({
|
||||
onChange={(e) => {
|
||||
setFee(null)
|
||||
setSelectedCoin(e.target.value)
|
||||
setStuckTrades([])
|
||||
}}
|
||||
>
|
||||
<MenuItem value={"LITECOIN"}>
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
Button,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
IconButton,
|
||||
InputLabel,
|
||||
@ -13,12 +14,16 @@ import {
|
||||
Typography,
|
||||
styled,
|
||||
} from "@mui/material";
|
||||
import React, { useContext } from "react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { BootstrapDialog } from "../Terms";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import { Spacer } from "../common/Spacer";
|
||||
import gameContext from "../../contexts/gameContext";
|
||||
import TradeBotList from "./TradeBotList";
|
||||
import { stuckTradesAtom } from "../../global/state";
|
||||
import { useAtom } from "jotai/react";
|
||||
import { StuckOrdersTable } from "./StuckOrdersTable";
|
||||
import { useGlobal } from "qapp-core";
|
||||
|
||||
export const CustomLabel = styled(InputLabel)`
|
||||
font-weight: 400;
|
||||
@ -97,6 +102,7 @@ export const CustomInput = styled(TextField)({
|
||||
|
||||
export const CreateSell = ({ qortAddress, show }) => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [openStuckOrders, setOpenStuckOrders] = React.useState(false);
|
||||
const [qortAmount, setQortAmount] = React.useState(0);
|
||||
const [foreignAmount, setForeignAmount] = React.useState(0);
|
||||
const {
|
||||
@ -220,7 +226,21 @@ export const CreateSell = ({ qortAddress, show }) => {
|
||||
display: show ? "block" : "none",
|
||||
}}
|
||||
>
|
||||
<Button onClick={handleClickOpen}>New Sell Order</Button>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
}}>
|
||||
<Button sx={{
|
||||
margin: '10px 0px'
|
||||
}} variant="outlined" onClick={handleClickOpen}>New Sell Order</Button>
|
||||
{!isUsingGateway && (
|
||||
<Button sx={{
|
||||
margin: '10px 0px'
|
||||
}} variant="outlined" onClick={()=> setOpenStuckOrders(true)}>Stuck orders</Button>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
<TradeBotList
|
||||
qortAddress={qortAddress}
|
||||
failedTradeBots={sellOrders.filter((item) => item.status === "FAILED")}
|
||||
@ -325,6 +345,55 @@ export const CreateSell = ({ qortAddress, show }) => {
|
||||
{info?.message}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
{openStuckOrders && (
|
||||
<StuckOrders setOpenStuckOrders={setOpenStuckOrders} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const StuckOrders = ({setOpenStuckOrders})=> {
|
||||
const [stuckTrades] = useAtom(stuckTradesAtom)
|
||||
const address = useGlobal().auth.address
|
||||
const filteredByAddress = stuckTrades?.filter((item)=> item?.qortalCreator === address)
|
||||
|
||||
return (
|
||||
<BootstrapDialog
|
||||
aria-labelledby="customized-dialog-title"
|
||||
open={true}
|
||||
maxWidth="lg"
|
||||
fullWidth={true}
|
||||
>
|
||||
<DialogTitle sx={{ m: 0, p: 2 }} id="customized-dialog-title">
|
||||
Stuck sell orders
|
||||
</DialogTitle>
|
||||
<IconButton
|
||||
aria-label="close"
|
||||
onClick={()=> setOpenStuckOrders(false)}
|
||||
sx={(theme) => ({
|
||||
position: "absolute",
|
||||
right: 8,
|
||||
top: 8,
|
||||
color: theme.palette.grey[500],
|
||||
})}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<DialogContent dividers>
|
||||
<DialogContentText></DialogContentText>
|
||||
<Spacer height="20px" />
|
||||
{filteredByAddress?.length === 0 && (
|
||||
<DialogContentText>No stuck trades</DialogContentText>
|
||||
)}
|
||||
<Spacer height="20px" />
|
||||
<StuckOrdersTable data={filteredByAddress} />
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button autoFocus onClick={()=> setOpenStuckOrders(false)}>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</BootstrapDialog>
|
||||
)
|
||||
}
|
@ -83,7 +83,10 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [recommendedFee, setRecommendedFee] = useState("medium_fee_per_kb");
|
||||
|
||||
const {hideRecommendations, recommendedFeeDisplay} = useRecommendedFees({selectedCoin, recommendedFee})
|
||||
const { hideRecommendations, recommendedFeeDisplay } = useRecommendedFees({
|
||||
selectedCoin,
|
||||
recommendedFee,
|
||||
});
|
||||
|
||||
const [openAlert, setOpenAlert] = useState(false);
|
||||
const [info, setInfo] = useState<any>(null);
|
||||
@ -143,10 +146,6 @@ const {hideRecommendations, recommendedFeeDisplay} = useRecommendedFees({selecte
|
||||
establishUpdateFeeForm(coin);
|
||||
}, [coin, establishUpdateFeeForm]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (hideRecommendations) {
|
||||
setRecommendedFee("custom");
|
||||
@ -158,13 +157,12 @@ const {hideRecommendations, recommendedFeeDisplay} = useRecommendedFees({selecte
|
||||
const typeRequestLocking = "feekb";
|
||||
|
||||
try {
|
||||
|
||||
let feeToSave = editFee;
|
||||
if (recommendedFee !== "custom") {
|
||||
feeToSave = calculateFeeFromRate(recommendedFeeDisplay, 300);
|
||||
}
|
||||
if(+fee === +feeToSave){
|
||||
return
|
||||
if (+fee === +feeToSave) {
|
||||
return;
|
||||
}
|
||||
const response = await qortalRequestWithTimeout(
|
||||
{
|
||||
@ -215,8 +213,6 @@ const {hideRecommendations, recommendedFeeDisplay} = useRecommendedFees({selecte
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
if (fee === null || fee === undefined) return;
|
||||
return (
|
||||
<>
|
||||
@ -274,6 +270,18 @@ const {hideRecommendations, recommendedFeeDisplay} = useRecommendedFees({selecte
|
||||
</Typography>
|
||||
</HeaderRow>
|
||||
</CoinActionRow>
|
||||
<CoinActionRow>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography>current fee: {fee}</Typography>
|
||||
</Box>
|
||||
</CoinActionRow>
|
||||
<CoinActionRow>
|
||||
<HeaderRow>
|
||||
<Box
|
||||
|
@ -52,11 +52,14 @@ export const Settings = () => {
|
||||
const [lockingFee, setLockingFee] = useState("");
|
||||
const [recommendedFee, setRecommendedFee] = useState("medium_fee_per_kb");
|
||||
const [selectedCoin, setSelectedCoin] = useState("LITECOIN");
|
||||
|
||||
const { hideRecommendations, recommendedFeeDisplay, coin } = useRecommendedFees({
|
||||
selectedCoin,
|
||||
recommendedFee
|
||||
});
|
||||
const {
|
||||
isUsingGateway
|
||||
} = useContext(gameContext);
|
||||
const { hideRecommendations, recommendedFeeDisplay, coin } =
|
||||
useRecommendedFees({
|
||||
selectedCoin,
|
||||
recommendedFee,
|
||||
});
|
||||
|
||||
const [editLockingFee, setEditLockingFee] = useState("");
|
||||
const [openAlert, setOpenAlert] = useState(false);
|
||||
@ -85,7 +88,7 @@ export const Settings = () => {
|
||||
try {
|
||||
let feeToSave = editLockingFee;
|
||||
if (recommendedFee !== "custom") {
|
||||
feeToSave = recommendedFeeDisplay
|
||||
feeToSave = recommendedFeeDisplay;
|
||||
}
|
||||
const response = await qortalRequestWithTimeout(
|
||||
{
|
||||
@ -183,11 +186,11 @@ export const Settings = () => {
|
||||
getSavedSelectedPublisher();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (hideRecommendations) {
|
||||
setRecommendedFee("custom");
|
||||
}
|
||||
}, [hideRecommendations]);
|
||||
useEffect(() => {
|
||||
if (hideRecommendations) {
|
||||
setRecommendedFee("custom");
|
||||
}
|
||||
}, [hideRecommendations]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -218,197 +221,206 @@ export const Settings = () => {
|
||||
}}
|
||||
open={openModal}
|
||||
>
|
||||
<CoinActionContainer
|
||||
sx={{
|
||||
border: "1px solid #3F3F3F",
|
||||
borderRadius: "5px",
|
||||
padding: "5px",
|
||||
}}
|
||||
>
|
||||
<Typography>Locking fees</Typography>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={isEnabledCustomLockingFee}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
}
|
||||
label="Enable custom locking fee"
|
||||
/>
|
||||
|
||||
{isEnabledCustomLockingFee && (
|
||||
<>
|
||||
<CoinSelectRow
|
||||
sx={{
|
||||
gap: "20px",
|
||||
width: '100%',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<Select
|
||||
size="small"
|
||||
value={selectedCoin}
|
||||
onChange={(e) => {
|
||||
setLockingFee("");
|
||||
setEditLockingFee("");
|
||||
setSelectedCoin(e.target.value);
|
||||
}}
|
||||
>
|
||||
<MenuItem value={"LITECOIN"}>
|
||||
<SelectRow coin="LTC" />
|
||||
</MenuItem>
|
||||
<MenuItem value={"DOGECOIN"}>
|
||||
<SelectRow coin="DOGE" />
|
||||
</MenuItem>
|
||||
<MenuItem value={"BITCOIN"}>
|
||||
<SelectRow coin="BTC" />
|
||||
</MenuItem>
|
||||
<MenuItem value={"DIGIBYTE"}>
|
||||
<SelectRow coin="DGB" />
|
||||
</MenuItem>
|
||||
<MenuItem value={"RAVENCOIN"}>
|
||||
<SelectRow coin="RVN" />
|
||||
</MenuItem>
|
||||
<MenuItem value={"PIRATECHAIN"}>
|
||||
<SelectRow coin="ARRR" />
|
||||
</MenuItem>
|
||||
</Select>
|
||||
<Box>
|
||||
|
||||
</Box>
|
||||
</CoinSelectRow>
|
||||
<CoinActionRow>
|
||||
<HeaderRow>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<CustomLabel
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
}}
|
||||
htmlFor="standard-adornment-name"
|
||||
>
|
||||
Recommended fee selection (in sats per kb)
|
||||
</CustomLabel>
|
||||
|
||||
<Spacer height="10px" />
|
||||
<ToggleButtonGroup
|
||||
color="primary"
|
||||
value={recommendedFee}
|
||||
exclusive
|
||||
onChange={handleChangeRecommended}
|
||||
aria-label="Platform"
|
||||
>
|
||||
{!hideRecommendations && (
|
||||
<>
|
||||
<ToggleButton value="low_fee_per_kb">
|
||||
Low
|
||||
</ToggleButton>
|
||||
<ToggleButton value="medium_fee_per_kb">
|
||||
Medium
|
||||
</ToggleButton>
|
||||
<ToggleButton value="high_fee_per_kb">
|
||||
High
|
||||
</ToggleButton>
|
||||
</>
|
||||
)}
|
||||
|
||||
<ToggleButton value="custom">Custom</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Box>
|
||||
{recommendedFeeDisplay && (
|
||||
<>
|
||||
<Spacer height="15px" />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "white",
|
||||
fontSize: "18px",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
New fee:
|
||||
</span>{" "}
|
||||
{recommendedFeeDisplay}{" "}
|
||||
sats per kb
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Spacer height="10px" />
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</HeaderRow>
|
||||
</CoinActionRow>
|
||||
{recommendedFee === "custom" && (
|
||||
<CoinActionRow>
|
||||
<HeaderRow>
|
||||
<Box>
|
||||
<CustomLabel htmlFor="standard-adornment-name">
|
||||
Custom fee
|
||||
</CustomLabel>
|
||||
<Spacer height="5px" />
|
||||
<CustomInput
|
||||
id="standard-adornment-name"
|
||||
type="number"
|
||||
value={editLockingFee}
|
||||
onChange={(e) => setEditLockingFee(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</Box>
|
||||
</HeaderRow>
|
||||
</CoinActionRow>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
<ButtonBase
|
||||
onClick={updateLockingFee}
|
||||
disabled={recommendedFee === "custom" && !editLockingFee}
|
||||
sx={{
|
||||
minHeight: "42px",
|
||||
border: "1px solid gray",
|
||||
color: "white",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "5px 20px",
|
||||
gap: "10px",
|
||||
borderRadius: "5px",
|
||||
"&:hover": {
|
||||
border: "1px solid white", // Border color on hover
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ChangeCircleIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
<Typography>Update locking fee</Typography>
|
||||
</ButtonBase>
|
||||
</CoinActionContainer>
|
||||
{!isUsingGateway && (
|
||||
<CoinActionContainer
|
||||
sx={{
|
||||
border: "1px solid #3F3F3F",
|
||||
borderRadius: "5px",
|
||||
padding: "5px",
|
||||
}}
|
||||
>
|
||||
<Typography>Locking fees</Typography>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={isEnabledCustomLockingFee}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
}
|
||||
label="Enable custom locking fee"
|
||||
/>
|
||||
|
||||
{isEnabledCustomLockingFee && (
|
||||
<>
|
||||
<CoinSelectRow
|
||||
sx={{
|
||||
gap: "20px",
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Select
|
||||
size="small"
|
||||
value={selectedCoin}
|
||||
onChange={(e) => {
|
||||
setLockingFee("");
|
||||
setEditLockingFee("");
|
||||
setSelectedCoin(e.target.value);
|
||||
}}
|
||||
>
|
||||
<MenuItem value={"LITECOIN"}>
|
||||
<SelectRow coin="LTC" />
|
||||
</MenuItem>
|
||||
<MenuItem value={"DOGECOIN"}>
|
||||
<SelectRow coin="DOGE" />
|
||||
</MenuItem>
|
||||
<MenuItem value={"BITCOIN"}>
|
||||
<SelectRow coin="BTC" />
|
||||
</MenuItem>
|
||||
<MenuItem value={"DIGIBYTE"}>
|
||||
<SelectRow coin="DGB" />
|
||||
</MenuItem>
|
||||
<MenuItem value={"RAVENCOIN"}>
|
||||
<SelectRow coin="RVN" />
|
||||
</MenuItem>
|
||||
<MenuItem value={"PIRATECHAIN"}>
|
||||
<SelectRow coin="ARRR" />
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</CoinSelectRow>
|
||||
<CoinSelectRow>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography>current fee: {lockingFee}</Typography>
|
||||
</Box>
|
||||
</CoinSelectRow>
|
||||
<CoinActionRow>
|
||||
<HeaderRow>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<CustomLabel
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
}}
|
||||
htmlFor="standard-adornment-name"
|
||||
>
|
||||
Recommended fee selection (in sats per kb)
|
||||
</CustomLabel>
|
||||
|
||||
<Spacer height="10px" />
|
||||
<ToggleButtonGroup
|
||||
color="primary"
|
||||
value={recommendedFee}
|
||||
exclusive
|
||||
onChange={handleChangeRecommended}
|
||||
aria-label="Platform"
|
||||
>
|
||||
{!hideRecommendations && (
|
||||
<>
|
||||
<ToggleButton value="low_fee_per_kb">
|
||||
Low
|
||||
</ToggleButton>
|
||||
<ToggleButton value="medium_fee_per_kb">
|
||||
Medium
|
||||
</ToggleButton>
|
||||
<ToggleButton value="high_fee_per_kb">
|
||||
High
|
||||
</ToggleButton>
|
||||
</>
|
||||
)}
|
||||
|
||||
<ToggleButton value="custom">Custom</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Box>
|
||||
{recommendedFeeDisplay && (
|
||||
<>
|
||||
<Spacer height="15px" />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "white",
|
||||
fontSize: "18px",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
New fee:
|
||||
</span>{" "}
|
||||
{recommendedFeeDisplay} sats per kb
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Spacer height="10px" />
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</HeaderRow>
|
||||
</CoinActionRow>
|
||||
{recommendedFee === "custom" && (
|
||||
<CoinActionRow>
|
||||
<HeaderRow>
|
||||
<Box>
|
||||
<CustomLabel htmlFor="standard-adornment-name">
|
||||
Custom fee
|
||||
</CustomLabel>
|
||||
<Spacer height="5px" />
|
||||
<CustomInput
|
||||
id="standard-adornment-name"
|
||||
type="number"
|
||||
value={editLockingFee}
|
||||
onChange={(e) => setEditLockingFee(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</Box>
|
||||
</HeaderRow>
|
||||
</CoinActionRow>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<ButtonBase
|
||||
onClick={updateLockingFee}
|
||||
disabled={recommendedFee === "custom" && !editLockingFee}
|
||||
sx={{
|
||||
minHeight: "42px",
|
||||
border: "1px solid gray",
|
||||
color: "white",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "5px 20px",
|
||||
gap: "10px",
|
||||
borderRadius: "5px",
|
||||
"&:hover": {
|
||||
border: "1px solid white", // Border color on hover
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ChangeCircleIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
<Typography>Update locking fee</Typography>
|
||||
</ButtonBase>
|
||||
</CoinActionContainer>
|
||||
)}
|
||||
<Spacer height="20px" />
|
||||
<CoinActionContainer
|
||||
sx={{
|
||||
|
118
src/components/sell/StuckOrdersTable.tsx
Normal file
118
src/components/sell/StuckOrdersTable.tsx
Normal file
@ -0,0 +1,118 @@
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Paper,
|
||||
Button,
|
||||
} from "@mui/material";
|
||||
import { forwardRef } from "react";
|
||||
import { TableVirtuoso, TableComponents } from "react-virtuoso";
|
||||
import {
|
||||
dismissToast,
|
||||
showError,
|
||||
showLoading,
|
||||
showSuccess,
|
||||
useGlobal,
|
||||
} from "qapp-core";
|
||||
import { formatTimestampForum } from "../../utils/formatTime";
|
||||
|
||||
interface NameData {
|
||||
name: string;
|
||||
isSelling?: boolean;
|
||||
}
|
||||
|
||||
const VirtuosoTableComponents: TableComponents<NameData> = {
|
||||
Scroller: forwardRef<HTMLDivElement>((props, ref) => (
|
||||
<TableContainer component={Paper} {...props} ref={ref} />
|
||||
)),
|
||||
Table: (props) => (
|
||||
<Table
|
||||
{...props}
|
||||
sx={{ borderCollapse: "separate", tableLayout: "fixed" }}
|
||||
/>
|
||||
),
|
||||
TableHead: forwardRef<HTMLTableSectionElement>((props, ref) => (
|
||||
<TableHead {...props} ref={ref} />
|
||||
)),
|
||||
TableRow,
|
||||
TableBody: forwardRef<HTMLTableSectionElement>((props, ref) => (
|
||||
<TableBody {...props} ref={ref} />
|
||||
)),
|
||||
};
|
||||
|
||||
function fixedHeaderContent() {
|
||||
return (
|
||||
<TableRow sx={{ backgroundColor: "background.paper" }}>
|
||||
<TableCell>Date</TableCell>
|
||||
<TableCell>Amount (QORT)</TableCell>
|
||||
<TableCell>Actions</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
function rowContent(_index: number, row: NameData) {
|
||||
const cancelSell = async (name: string) => {
|
||||
const loadId = showLoading("Attempting to cancel sell...please wait");
|
||||
|
||||
try {
|
||||
const res = await qortalRequestWithTimeout(
|
||||
{
|
||||
action: "CANCEL_TRADE_SELL_ORDER",
|
||||
qortAmount: row.qortAmount,
|
||||
foreignBlockchain: row.foreignBlockchain,
|
||||
foreignAmount: row.foreignAmount,
|
||||
atAddress: row.qortalAtAddress,
|
||||
},
|
||||
900000
|
||||
);
|
||||
if (res?.signature) {
|
||||
showSuccess(
|
||||
"Canceled sell order. It might take awhile for the cancel sell order changes to show up."
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
showError(error?.message || "Unable to cancel sell order");
|
||||
|
||||
console.log("error", error);
|
||||
} finally {
|
||||
dismissToast(loadId);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableCell>{formatTimestampForum(row?.timestamp)}</TableCell>
|
||||
<TableCell>{row?.qortAmount}</TableCell>
|
||||
<TableCell>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="small"
|
||||
onClick={() => cancelSell(row.name)}
|
||||
>
|
||||
Cancel sell
|
||||
</Button>
|
||||
</TableCell>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const StuckOrdersTable = ({ data }) => {
|
||||
return (
|
||||
<Paper
|
||||
sx={{
|
||||
height: "80vh", // Header + footer height
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<TableVirtuoso
|
||||
data={data}
|
||||
components={VirtuosoTableComponents}
|
||||
fixedHeaderContent={fixedHeaderContent}
|
||||
itemContent={(index, row) => rowContent(index, row)}
|
||||
/>
|
||||
</Paper>
|
||||
);
|
||||
};
|
@ -5,3 +5,5 @@ import { atomWithReset } from 'jotai/utils';
|
||||
export const selectedFeePublisherAtom = atomWithReset('Ice.JSON');
|
||||
|
||||
export const isEnabledCustomLockingFeeAtom = atomWithReset(false);
|
||||
|
||||
export const stuckTradesAtom = atomWithReset([]);
|
||||
|
Loading…
x
Reference in New Issue
Block a user