Fixed bottom UI buy container

This commit is contained in:
Justin Ferrari 2024-12-26 22:52:47 -05:00
parent 7ef7a8b73f
commit a52308d838
6 changed files with 544 additions and 402 deletions

View File

@ -1,5 +1,12 @@
import { Box, styled } from "@mui/system"; import { Box, styled } from "@mui/system";
import { Typography } from "@mui/material"; import { Button, Typography } from "@mui/material";
export const MainContainer = styled(Box)({
display: "flex",
flexDirection: "column",
width: "100%",
height: "100%",
});
export const TextTableTitle = styled(Typography)(({ theme }) => ({ export const TextTableTitle = styled(Typography)(({ theme }) => ({
fontFamily: "Inter", fontFamily: "Inter",
@ -11,14 +18,39 @@ export const TextTableTitle = styled(Typography)(({ theme }) => ({
})); }));
export const BuyContainer = styled(Box)({ export const BuyContainer = styled(Box)({
width: "calc(100% - 60px)", position: "fixed",
width: "calc(100% - 14px)",
display: "flex", display: "flex",
justifyContent: "space-between", justifyContent: "space-between",
alignItems: "center", alignItems: "center",
position: "fixed",
bottom: "0px", bottom: "0px",
height: "100px", height: "100px",
padding: "7px 14px", padding: "18px 14px 12px 14px",
background: "#323336", background: "#323336",
zIndex: 3 zIndex: 3,
}); });
export const BuyContainerDivider = styled(Box)({
position: "absolute",
width: "60%",
height: "1px",
background: "lightgray",
top: "10px",
left: "50%",
transform: "translateX(-50%)",
});
export const BuyOrderBtn = styled("button")(({ theme }) => ({
borderRadius: "8px",
width: "74px",
height: "30px",
background: "#4D7345",
color: "white",
cursor: "pointer",
border: "1px solid #375232",
boxShadow: "0px 2.77px 2.21px 0px #00000005",
marginRight: "10px",
[theme.breakpoints.down("sm")]: {
marginRight: "5px",
}
}));

View File

@ -15,8 +15,6 @@ import {
} from "ag-grid-community"; } from "ag-grid-community";
import "ag-grid-community/styles/ag-grid.css"; import "ag-grid-community/styles/ag-grid.css";
import "ag-grid-community/styles/ag-theme-alpine.css"; import "ag-grid-community/styles/ag-theme-alpine.css";
import axios from "axios";
import { sendRequestToExtension } from "../../App";
import { import {
Alert, Alert,
Box, Box,
@ -39,9 +37,15 @@ import { Hourglass } from "react-loader-spinner";
import ErrorIcon from "@mui/icons-material/Error"; import ErrorIcon from "@mui/icons-material/Error";
import CheckCircleIcon from "@mui/icons-material/CheckCircle"; import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import { CountdownCircleTimer } from "react-countdown-circle-timer"; import { CountdownCircleTimer } from "react-countdown-circle-timer";
import { BuyContainer } from "./Table-styles"; import {
BuyContainer,
BuyContainerDivider,
BuyOrderBtn,
ContentArea,
MainContainer,
} from "./Table-styles";
export const baseLocalHost = window.location.host export const baseLocalHost = window.location.host;
// export const baseLocalHost = "127.0.0.1:12391"; // export const baseLocalHost = "127.0.0.1:12391";
interface RowData { interface RowData {
@ -110,23 +114,7 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [info, setInfo] = useState<any>(null); const [info, setInfo] = useState<any>(null);
const BuyButton = () => { const BuyButton = () => {
return ( return <BuyOrderBtn onClick={buyOrder}>BUY</BuyOrderBtn>;
<button
onClick={buyOrder}
style={{
borderRadius: "8px",
width: "74px",
height: "30px",
background: "#4D7345",
color: "white",
cursor: "pointer",
border: "1px solid #375232",
boxShadow: "0px 2.77px 2.21px 0px #00000005",
}}
>
BUY
</button>
);
}; };
const defaultColDef = { const defaultColDef = {
@ -500,12 +488,12 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
const buyOrder = async () => { const buyOrder = async () => {
try { try {
if (+foreignCoinBalance < +selectedTotalLTC.toFixed(4)) { if (+foreignCoinBalance < +selectedTotalLTC.toFixed(4)) {
setOpen(true) setOpen(true);
setInfo({ setInfo({
type: 'error', type: "error",
message: `You don't have enough ${getCoinLabel()} or your balance was not retrieved` message: `You don't have enough ${getCoinLabel()} or your balance was not retrieved`,
}) });
return return;
} }
if (selectedOffers?.length < 1) return; if (selectedOffers?.length < 1) return;
@ -653,11 +641,7 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
}; };
return ( return (
<Box <MainContainer>
sx={{
width: "100%",
}}
>
<div <div
className="ag-theme-alpine-dark" className="ag-theme-alpine-dark"
style={{ height: 400, width: "100%" }} style={{ height: 400, width: "100%" }}
@ -686,47 +670,75 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
)} */} )} */}
</div> </div>
<div style={{ <div
height: '120px' style={{
}} /> height: "120px",
}}
/>
<BuyContainer> <BuyContainer>
<Box sx={{ <BuyContainerDivider />
display: 'flex', <Box
gap: '5px', sx={{
flexDirection: 'column', display: "flex",
width: '100%' gap: "5px",
}}> flexDirection: "column",
<Typography sx={{ width: "100%",
fontSize: '16px', }}
color: 'white', >
width: 'calc(100% - 75px)' <Typography
}}>{selectedTotalQORT?.toFixed(3)} QORT</Typography> sx={{
<Box sx={{ fontSize: "16px",
display: 'flex', color: "white",
gap: '20px', width: "calc(100% - 75px)",
alignItems: 'center', }}
width: 'calc(100% - 75px)' >
}}> {selectedTotalQORT?.toFixed(3)} QORT
<Typography sx={{ </Typography>
fontSize: '16px', <Box
color: selectedTotalLTC > foreignCoinBalance ? 'red' : 'white', sx={{
}}><span>{selectedTotalLTC?.toFixed(4)}</span> <span style={{ display: "flex",
marginLeft: 'auto' gap: "20px",
}}>{`${getCoinLabel()} `}</span></Typography> alignItems: "center",
width: "calc(100% - 75px)",
}}
>
<Typography
sx={{
fontSize: "16px",
color: selectedTotalLTC > foreignCoinBalance ? "red" : "white",
}}
>
<span>{selectedTotalLTC?.toFixed(4)}</span>{" "}
<span
style={{
marginLeft: "auto",
}}
>{`${getCoinLabel()} `}</span>
</Typography>
</Box> </Box>
<Typography sx={{ <Typography
fontSize: '16px', sx={{
color: 'white', fontSize: "16px",
color: "white",
}}><span>{foreignCoinBalance?.toFixed(4)}</span> <span style={{ }}
marginLeft: 'auto' >
}}>{`${getCoinLabel()} `} balance</span></Typography> <span>{foreignCoinBalance?.toFixed(4)}</span>{" "}
<span
style={{
marginLeft: "auto",
}}
>
{`${getCoinLabel()} `} balance
</span>
</Typography>
</Box> </Box>
{BuyButton()} {BuyButton()}
</BuyContainer> </BuyContainer>
<Snackbar anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} open={open} onClose={handleClose}> <Snackbar
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
open={open}
onClose={handleClose}
>
<Alert <Alert
onClose={handleClose} onClose={handleClose}
severity={info?.type} severity={info?.type}
@ -826,8 +838,7 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
</Typography> </Typography>
<Spacer height="20px" /> <Spacer height="20px" />
<Typography> <Typography>
You can see the progress of your You can see the progress of your order in the "Pending" table.
order in the "Pending" table.
</Typography> </Typography>
<Spacer height="20px" /> <Spacer height="20px" />
<Typography> <Typography>
@ -860,14 +871,17 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
{isUsingGateway && ( {isUsingGateway && (
<> <>
<Typography> <Typography>
Using gateway: might take up to 3 minutes to submit the buy order. Using gateway: might take up to 3 minutes to submit the
buy order.
</Typography> </Typography>
<Spacer height="20px" /> <Spacer height="20px" />
<Box sx={{ <Box
width: '100%', sx={{
display: 'flex', width: "100%",
justifyContent: 'center' display: "flex",
}}> justifyContent: "center",
}}
>
<CountdownCircleTimer <CountdownCircleTimer
isPlaying isPlaying
duration={180} duration={180}
@ -879,7 +893,9 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
size={60} size={60}
strokeWidth={4} strokeWidth={4}
> >
{({ remainingTime }) => <Typography>{remainingTime}</Typography>} {({ remainingTime }) => (
<Typography>{remainingTime}</Typography>
)}
</CountdownCircleTimer> </CountdownCircleTimer>
</Box> </Box>
</> </>
@ -905,6 +921,6 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
</DialogActions> </DialogActions>
</Dialog> </Dialog>
)} )}
</Box> </MainContainer>
); );
}; };

View File

@ -1,10 +1,24 @@
import { Alert, Box, Button, DialogActions, DialogContent, DialogTitle, IconButton, InputLabel, Snackbar, SnackbarCloseReason, TextField, Typography, styled } from '@mui/material' import {
import React, { useContext } from 'react' Alert,
import { BootstrapDialog } from '../Terms' Box,
import CloseIcon from '@mui/icons-material/Close'; Button,
import { Spacer } from '../common/Spacer'; DialogActions,
import gameContext from '../../contexts/gameContext'; DialogContent,
import TradeBotList from './TradeBotList'; DialogTitle,
IconButton,
InputLabel,
Snackbar,
SnackbarCloseReason,
TextField,
Typography,
styled,
} from "@mui/material";
import React, { useContext } 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";
export const CustomLabel = styled(InputLabel)` export const CustomLabel = styled(InputLabel)`
font-weight: 400; font-weight: 400;
@ -12,13 +26,12 @@ export const CustomLabel = styled(InputLabel)`
font-size: 10px; font-size: 10px;
line-height: 12px; line-height: 12px;
color: rgba(255, 255, 255, 0.5); color: rgba(255, 255, 255, 0.5);
`;
`
export const minimumAmountSellTrades = { export const minimumAmountSellTrades = {
'LITECOIN': { LITECOIN: {
value: 0.01, value: 0.01,
ticker: 'LTC' ticker: "LTC",
}, },
DOGECOIN: { DOGECOIN: {
value: 1, value: 1,
@ -40,7 +53,7 @@ export const minimumAmountSellTrades = {
value: 0.0002, value: 0.0002,
ticker: "ARRR", ticker: "ARRR",
}, },
} };
export const CustomInput = styled(TextField)({ export const CustomInput = styled(TextField)({
width: "183px", // Adjust the width as needed width: "183px", // Adjust the width as needed
@ -61,13 +74,13 @@ export const CustomInput = styled(TextField)({
}, },
"& .MuiOutlinedInput-root": { "& .MuiOutlinedInput-root": {
"& fieldset": { "& fieldset": {
border: '0.5px solid rgba(255, 255, 255, 0.5)', border: "0.5px solid rgba(255, 255, 255, 0.5)",
}, },
"&:hover fieldset": { "&:hover fieldset": {
border: '0.5px solid rgba(255, 255, 255, 0.5)', border: "0.5px solid rgba(255, 255, 255, 0.5)",
}, },
"&.Mui-focused fieldset": { "&.Mui-focused fieldset": {
border: '0.5px solid rgba(255, 255, 255, 0.5)', border: "0.5px solid rgba(255, 255, 255, 0.5)",
}, },
}, },
"& .MuiInput-underline:before": { "& .MuiInput-underline:before": {
@ -81,126 +94,144 @@ export const CustomInput = styled(TextField)({
}, },
}); });
export const CreateSell = ({ qortAddress, show }) => { export const CreateSell = ({ qortAddress, show }) => {
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const [qortAmount, setQortAmount] = React.useState(0) const [qortAmount, setQortAmount] = React.useState(0);
const [foreignAmount, setForeignAmount] = React.useState(0) const [foreignAmount, setForeignAmount] = React.useState(0);
const {updateTemporaryFailedTradeBots, sellOrders, fetchTemporarySellOrders, isUsingGateway, getCoinLabel, selectedCoin} = useContext(gameContext) const {
const [openAlert, setOpenAlert] = React.useState(false) updateTemporaryFailedTradeBots,
const [info, setInfo] = React.useState<any>(null) sellOrders,
fetchTemporarySellOrders,
isUsingGateway,
getCoinLabel,
selectedCoin,
} = useContext(gameContext);
const [openAlert, setOpenAlert] = React.useState(false);
const [info, setInfo] = React.useState<any>(null);
const handleClickOpen = () => { const handleClickOpen = () => {
setOpen(true); setOpen(true);
}; };
const handleClose = () => { const handleClose = () => {
setOpen(false); setOpen(false);
setForeignAmount(0) setForeignAmount(0);
setQortAmount(0) setQortAmount(0);
}; };
const createSellOrder = async () => { const createSellOrder = async () => {
try { try {
setInfo({ setInfo({
type: 'info', type: "info",
message: "Attempting to create sell order. Please wait..." message: "Attempting to create sell order. Please wait...",
}) });
const res = await qortalRequestWithTimeout({ const res = await qortalRequestWithTimeout(
{
action: "CREATE_TRADE_SELL_ORDER", action: "CREATE_TRADE_SELL_ORDER",
qortAmount, qortAmount,
foreignBlockchain: selectedCoin, foreignBlockchain: selectedCoin,
foreignAmount: qortAmount * foreignAmount foreignAmount: qortAmount * foreignAmount,
}, 900000); },
900000
);
if (res?.error && res?.failedTradeBot) { if (res?.error && res?.failedTradeBot) {
await updateTemporaryFailedTradeBots({ await updateTemporaryFailedTradeBots({
atAddress: res?.failedTradeBot?.atAddress, atAddress: res?.failedTradeBot?.atAddress,
status: 'FAILED', status: "FAILED",
qortAddress: res?.failedTradeBot?.creatorAddress, qortAddress: res?.failedTradeBot?.creatorAddress,
});
}) fetchTemporarySellOrders();
fetchTemporarySellOrders() setOpenAlert(true);
setOpenAlert(true)
setInfo({ setInfo({
type: 'error', type: "error",
message: "Unable to create sell order. Please try again." message: "Unable to create sell order. Please try again.",
}) });
} }
if (!res?.error) { if (!res?.error) {
setOpenAlert(true) setOpenAlert(true);
setForeignAmount(0) setForeignAmount(0);
setQortAmount(0) setQortAmount(0);
setOpen(false) setOpen(false);
setInfo({ setInfo({
type: 'success', type: "success",
message: "Sell order created. Please wait a couple of minutes for the network to propogate the changes." message:
}) "Sell order created. Please wait a couple of minutes for the network to propogate the changes.",
});
} }
} catch (error) { } catch (error) {
if (error?.error && error?.failedTradeBot) { if (error?.error && error?.failedTradeBot) {
await updateTemporaryFailedTradeBots({ await updateTemporaryFailedTradeBots({
atAddress: error?.failedTradeBot?.atAddress, atAddress: error?.failedTradeBot?.atAddress,
status: 'FAILED', status: "FAILED",
qortAddress: error?.failedTradeBot?.creatorAddress, qortAddress: error?.failedTradeBot?.creatorAddress,
});
}) fetchTemporarySellOrders();
fetchTemporarySellOrders() setOpenAlert(true);
setOpenAlert(true)
setInfo({ setInfo({
type: 'error', type: "error",
message: "Unable to create sell order. Please try again." message: "Unable to create sell order. Please try again.",
}) });
}
} }
} }
};
const handleCloseAlert = ( const handleCloseAlert = (
event?: React.SyntheticEvent | Event, event?: React.SyntheticEvent | Event,
reason?: SnackbarCloseReason, reason?: SnackbarCloseReason
) => { ) => {
if (reason === 'clickaway') { if (reason === "clickaway") {
return; return;
} }
setOpenAlert(false); setOpenAlert(false);
setInfo(null) setInfo(null);
}; };
if (isUsingGateway) { if (isUsingGateway) {
return ( return (
<div style={{ <div
width: '100%', style={{
display: show ? 'flex' : 'none', width: "100%",
height: '500px', display: show ? "flex" : "none",
alignItems: 'center', height: "500px",
justifyContent: 'center', alignItems: "flex-start",
}}> marginTop: "20px",
<Typography sx={{ justifyContent: "center",
color: 'white', }}
maxWidth: '340px', >
padding: '10px' <Typography
}}> sx={{
Managing your sell orders is not possible using a gateway node. Please switch to a local or custom node at the authentication page color: "white",
maxWidth: "340px",
padding: "10px",
}}
>
Managing your sell orders is not possible using a gateway node. Please
switch to a local or custom node at the authentication page
</Typography> </Typography>
</div> </div>
) );
} }
return ( return (
<div style={{ <div
width: '100%', style={{
display: show ? 'block' : 'none' width: "100%",
}}> display: show ? "block" : "none",
}}
>
<Button onClick={handleClickOpen}>New Sell Order</Button> <Button onClick={handleClickOpen}>New Sell Order</Button>
<TradeBotList qortAddress={qortAddress} failedTradeBots={sellOrders.filter((item)=> item.status === 'FAILED')} /> <TradeBotList
qortAddress={qortAddress}
failedTradeBots={sellOrders.filter((item) => item.status === "FAILED")}
/>
<BootstrapDialog <BootstrapDialog
onClose={handleClose} onClose={handleClose}
aria-labelledby="customized-dialog-title" aria-labelledby="customized-dialog-title"
open={open} open={open}
sx={{ sx={{
'& .MuiDialogContent-root': { "& .MuiDialogContent-root": {
width: '300px' width: "300px",
}, },
}} }}
> >
@ -211,7 +242,7 @@ export const CreateSell = ({qortAddress, show}) => {
aria-label="close" aria-label="close"
onClick={handleClose} onClick={handleClose}
sx={(theme) => ({ sx={(theme) => ({
position: 'absolute', position: "absolute",
right: 8, right: 8,
top: 8, top: 8,
color: theme.palette.grey[500], color: theme.palette.grey[500],
@ -221,7 +252,9 @@ export const CreateSell = ({qortAddress, show}) => {
</IconButton> </IconButton>
<DialogContent dividers> <DialogContent dividers>
<Box> <Box>
<CustomLabel htmlFor="standard-adornment-name">QORT amount</CustomLabel> <CustomLabel htmlFor="standard-adornment-name">
QORT amount
</CustomLabel>
<Spacer height="5px" /> <Spacer height="5px" />
<CustomInput <CustomInput
id="standard-adornment-name" id="standard-adornment-name"
@ -243,34 +276,54 @@ export const CreateSell = ({qortAddress, show}) => {
autoComplete="off" autoComplete="off"
/> />
<Spacer height="6px" /> <Spacer height="6px" />
<Typography>{`${qortAmount * foreignAmount} ${getCoinLabel()}`} for {qortAmount} QORT</Typography> <Typography>
<Typography sx={{ {`${qortAmount * foreignAmount} ${getCoinLabel()}`} for{" "}
fontSize: '14px' {qortAmount} QORT
}}>Total sell amount needs to be greater than: {minimumAmountSellTrades[selectedCoin]?.value} {' '} {minimumAmountSellTrades[selectedCoin]?.ticker}</Typography> </Typography>
<Typography
sx={{
fontSize: "14px",
}}
>
Total sell amount needs to be greater than:{" "}
{minimumAmountSellTrades[selectedCoin]?.value}{" "}
{minimumAmountSellTrades[selectedCoin]?.ticker}
</Typography>
</Box> </Box>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button autoFocus onClick={handleClose}> <Button autoFocus onClick={handleClose}>
Close Close
</Button> </Button>
<Button disabled={!qortAmount || !(qortAmount * foreignAmount > minimumAmountSellTrades[selectedCoin]?.value)} autoFocus onClick={createSellOrder}> <Button
disabled={
!qortAmount ||
!(
qortAmount * foreignAmount >
minimumAmountSellTrades[selectedCoin]?.value
)
}
autoFocus
onClick={createSellOrder}
>
Create sell order Create sell order
</Button> </Button>
</DialogActions> </DialogActions>
</BootstrapDialog> </BootstrapDialog>
<Snackbar anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} open={openAlert} onClose={handleCloseAlert}> <Snackbar
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
open={openAlert}
onClose={handleCloseAlert}
>
<Alert <Alert
onClose={handleCloseAlert} onClose={handleCloseAlert}
severity={info?.type} severity={info?.type}
variant="filled" variant="filled"
sx={{ width: '100%' }} sx={{ width: "100%" }}
> >
{info?.message} {info?.message}
</Alert> </Alert>
</Snackbar> </Snackbar>
</div> </div>
) );
} };

View File

@ -9,8 +9,15 @@ import React, {
useState, useState,
} from "react"; } from "react";
import { autoSizeStrategy, baseLocalHost } from "../Grids/TradeOffers"; import { autoSizeStrategy, baseLocalHost } from "../Grids/TradeOffers";
import { Alert, Box, Snackbar, SnackbarCloseReason, Typography } from "@mui/material"; import {
Alert,
Box,
Snackbar,
SnackbarCloseReason,
Typography,
} from "@mui/material";
import gameContext from "../../contexts/gameContext"; import gameContext from "../../contexts/gameContext";
import { BuyContainerDivider } from "../Grids/Table-styles";
const defaultColDef = { const defaultColDef = {
resizable: true, // Make columns resizable by default resizable: true, // Make columns resizable by default
@ -18,18 +25,22 @@ const defaultColDef = {
suppressMovable: true, // Prevent columns from being movable suppressMovable: true, // Prevent columns from being movable
}; };
export default function TradeBotList({ qortAddress, failedTradeBots }) { export default function TradeBotList({ qortAddress, failedTradeBots }) {
const [tradeBotList, setTradeBotList] = useState([]); const [tradeBotList, setTradeBotList] = useState([]);
const [selectedTrade, setSelectedTrade] = useState(null); const [selectedTrade, setSelectedTrade] = useState(null);
const tradeBotListRef = useRef([]) const tradeBotListRef = useRef([]);
const offeringTrades = useRef<any[]>([]); const offeringTrades = useRef<any[]>([]);
const qortAddressRef = useRef(null); const qortAddressRef = useRef(null);
const gridRef = useRef<any>(null); const gridRef = useRef<any>(null);
const {updateTemporaryFailedTradeBots, fetchTemporarySellOrders, deleteTemporarySellOrder, getCoinLabel, selectedCoin} = useContext(gameContext) const {
const [open, setOpen] = useState(false) updateTemporaryFailedTradeBots,
const [info, setInfo] = useState<any>(null) fetchTemporarySellOrders,
deleteTemporarySellOrder,
getCoinLabel,
selectedCoin,
} = useContext(gameContext);
const [open, setOpen] = useState(false);
const [info, setInfo] = useState<any>(null);
const filteredOutTradeBotListWithoutFailed = useMemo(() => { const filteredOutTradeBotListWithoutFailed = useMemo(() => {
const list = tradeBotList.filter( const list = tradeBotList.filter(
(item) => (item) =>
@ -37,7 +48,7 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) {
(failedItem) => failedItem.atAddress === item.atAddress (failedItem) => failedItem.atAddress === item.atAddress
) )
); );
return list return list;
}, [failedTradeBots, tradeBotList]); }, [failedTradeBots, tradeBotList]);
const onGridReady = useCallback((params: any) => { const onGridReady = useCallback((params: any) => {
@ -48,7 +59,6 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) {
params.columnApi.autoSizeColumns(allColumnIds); // Automatically adjust the width to fit content params.columnApi.autoSizeColumns(allColumnIds); // Automatically adjust the width to fit content
}, []); }, []);
const columnDefs: ColDef[] = useMemo(() => { const columnDefs: ColDef[] = useMemo(() => {
return [ return [
{ {
@ -91,8 +101,7 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) {
resizable: true, resizable: true,
}, },
]; ];
}, [selectedCoin]);
}, [selectedCoin])
useEffect(() => { useEffect(() => {
if (qortAddress) { if (qortAddress) {
qortAddressRef.current = qortAddress; qortAddressRef.current = qortAddress;
@ -161,21 +170,23 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) {
const restartTradeOffers = () => { const restartTradeOffers = () => {
if (socketRef.current) { if (socketRef.current) {
socketRef.current.close(1000, 'forced'); // Close with a custom reason socketRef.current.close(1000, "forced"); // Close with a custom reason
socketRef.current = null socketRef.current = null;
} }
offeringTrades.current = [] offeringTrades.current = [];
setTradeBotList([]); setTradeBotList([]);
tradeBotListRef.current = []; tradeBotListRef.current = [];
} };
const socketRef = useRef(null) const socketRef = useRef(null);
const initTradeOffersWebSocket = (restarted = false) => { const initTradeOffersWebSocket = (restarted = false) => {
let tradeOffersSocketCounter = 0; let tradeOffersSocketCounter = 0;
let socketTimeout: any; let socketTimeout: any;
// let socketLink = `ws://127.0.0.1:12391/websockets/crosschain/tradebot?foreignBlockchain=LITECOIN`; // let socketLink = `ws://127.0.0.1:12391/websockets/crosschain/tradebot?foreignBlockchain=LITECOIN`;
let socketLink = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${baseLocalHost}/websockets/crosschain/tradebot?foreignBlockchain=${selectedCoin}`; let socketLink = `${
window.location.protocol === "https:" ? "wss:" : "ws:"
}//${baseLocalHost}/websockets/crosschain/tradebot?foreignBlockchain=${selectedCoin}`;
socketRef.current = new WebSocket(socketLink); socketRef.current = new WebSocket(socketLink);
socketRef.current.onopen = () => { socketRef.current.onopen = () => {
setTimeout(pingSocket, 50); setTimeout(pingSocket, 50);
@ -188,8 +199,8 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) {
}; };
socketRef.current.onclose = (event) => { socketRef.current.onclose = (event) => {
clearTimeout(socketTimeout); clearTimeout(socketTimeout);
if (event.reason === 'forced') { if (event.reason === "forced") {
return return;
} }
restartTradeOffersWebSocket(); restartTradeOffersWebSocket();
}; };
@ -203,94 +214,113 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) {
}; };
useEffect(() => { useEffect(() => {
if(!qortAddress) return if (!qortAddress) return;
if(selectedCoin === null) return if (selectedCoin === null) return;
restartTradeOffers() restartTradeOffers();
setTimeout(() => { setTimeout(() => {
initTradeOffersWebSocket() initTradeOffersWebSocket();
}, 500); }, 500);
return () => { return () => {
if (socketRef.current) { if (socketRef.current) {
socketRef.current.close(1000, 'forced'); socketRef.current.close(1000, "forced");
}
} }
};
}, [qortAddress, selectedCoin]); }, [qortAddress, selectedCoin]);
const onSelectionChanged = (event: any) => { const onSelectionChanged = (event: any) => {
const selectedRows = event.api.getSelectedRows(); const selectedRows = event.api.getSelectedRows();
if (selectedRows[0]) { if (selectedRows[0]) {
setSelectedTrade(selectedRows[0]) setSelectedTrade(selectedRows[0]);
} else { } else {
setSelectedTrade(null) setSelectedTrade(null);
} }
}; };
const handleClose = ( const handleClose = (
event?: React.SyntheticEvent | Event, event?: React.SyntheticEvent | Event,
reason?: SnackbarCloseReason, reason?: SnackbarCloseReason
) => { ) => {
if (reason === 'clickaway') { if (reason === "clickaway") {
return; return;
} }
setOpen(false); setOpen(false);
setInfo(null) setInfo(null);
}; };
const cancelSell = async () => { const cancelSell = async () => {
try { try {
if(!selectedTrade) return if (!selectedTrade) return;
setOpen(true) setOpen(true);
setInfo({ setInfo({
type: 'info', type: "info",
message: "Attempting to cancel sell order" message: "Attempting to cancel sell order",
}) });
const res = await qortalRequestWithTimeout({ const res = await qortalRequestWithTimeout(
{
action: "CANCEL_TRADE_SELL_ORDER", action: "CANCEL_TRADE_SELL_ORDER",
qortAmount: selectedTrade.qortAmount, qortAmount: selectedTrade.qortAmount,
foreignBlockchain: selectedTrade.foreignBlockchain, foreignBlockchain: selectedTrade.foreignBlockchain,
foreignAmount: selectedTrade.foreignAmount, foreignAmount: selectedTrade.foreignAmount,
atAddress: selectedTrade.atAddress atAddress: selectedTrade.atAddress,
}, 900000); },
900000
);
if (res?.signature) { if (res?.signature) {
await deleteTemporarySellOrder(selectedTrade.atAddress) await deleteTemporarySellOrder(selectedTrade.atAddress);
setSelectedTrade(null);
setSelectedTrade(null) setOpen(true);
setOpen(true)
setInfo({ setInfo({
type: 'success', type: "success",
message: "Sell order canceled. Please wait a couple of minutes for the network to propogate the changes" message:
}) "Sell order canceled. Please wait a couple of minutes for the network to propogate the changes",
});
} }
if (res?.error && res?.failedTradeBot) { if (res?.error && res?.failedTradeBot) {
setOpen(true) setOpen(true);
setInfo({ setInfo({
type: 'error', type: "error",
message: "Unable to cancel sell order. Please try again." message: "Unable to cancel sell order. Please try again.",
}) });
} }
} catch (error) { } catch (error) {
if (error?.error && error?.failedTradeBot) { if (error?.error && error?.failedTradeBot) {
setOpen(true) setOpen(true);
setInfo({ setInfo({
type: 'error', type: "error",
message: "Unable to cancel sell order. Please try again." message: "Unable to cancel sell order. Please try again.",
}) });
}
} }
} }
};
const CancelButton = () => { const CancelButton = () => {
return ( return (
<button disabled={!selectedTrade || selectedTrade?.status === 'PENDING'} onClick={cancelSell} style={{borderRadius: '8px', width: '150px', height:"30px", background: (!selectedTrade || selectedTrade?.status === 'PENDING') ? 'gray' : "#4D7345", <button
color: 'white', cursor: (!selectedTrade || selectedTrade?.status === 'PENDING') ? 'default' : 'pointer', border: '1px solid #375232', boxShadow: '0px 2.77px 2.21px 0px #00000005' disabled={!selectedTrade || selectedTrade?.status === "PENDING"}
}}> onClick={cancelSell}
style={{
borderRadius: "8px",
width: "150px",
height: "auto",
minHeight: "30px",
background:
!selectedTrade || selectedTrade?.status === "PENDING"
? "gray"
: "#4D7345",
color: "white",
cursor:
!selectedTrade || selectedTrade?.status === "PENDING"
? "default"
: "pointer",
border: "1px solid #375232",
boxShadow: "0px 2.77px 2.21px 0px #00000005",
marginRight: "15px",
}}
>
Cancel sell order Cancel sell order
</button> </button>
); );
@ -329,43 +359,52 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) {
)} */} )} */}
</div> </div>
<Box sx={{ <div
width: '100%', style={{
display: 'flex', height: "120px",
justifyContent: 'space-between', }}
alignItems: 'center', />
position: 'fixed', <Box
bottom: '0px', sx={{
height: '100px', width: "calc(100% - 14px)",
padding: '7px', display: "flex",
background: '#181d1f', justifyContent: "space-between",
alignItems: "center",
}}> position: "fixed",
<Box sx={{ bottom: "0px",
display: 'flex', height: "100px",
gap: '5px', padding: "7px",
flexDirection: 'column', background: "#323336",
width: '100%' }}
}}> >
<BuyContainerDivider />
<Box
sx={{
display: "flex",
gap: "5px",
flexDirection: "column",
width: "100%",
}}
>
{/* <Typography sx={{ {/* <Typography sx={{
fontSize: '16px', fontSize: '16px',
color: 'white', color: 'white',
width: 'calc(100% - 75px)' width: 'calc(100% - 75px)'
}}>{selectedTotalQORT?.toFixed(3)} QORT</Typography> */} }}>{selectedTotalQORT?.toFixed(3)} QORT</Typography> */}
<Box sx={{ <Box
display: 'flex', sx={{
gap: '20px', display: "flex",
alignItems: 'center', gap: "20px",
width: 'calc(100% - 75px)' alignItems: "center",
}}> width: "calc(100% - 75px)",
}}
>
{/* <Typography sx={{ {/* <Typography sx={{
fontSize: '16px', fontSize: '16px',
color: selectedTotalLTC > foreignCoinBalance ? 'red' : 'white', color: selectedTotalLTC > foreignCoinBalance ? 'red' : 'white',
}}><span>{selectedTotalLTC?.toFixed(4)}</span> <span style={{ }}><span>{selectedTotalLTC?.toFixed(4)}</span> <span style={{
marginLeft: 'auto' marginLeft: 'auto'
}}>LTC</span></Typography> */} }}>LTC</span></Typography> */}
</Box> </Box>
{/* <Typography sx={{ {/* <Typography sx={{
fontSize: '16px', fontSize: '16px',
@ -377,14 +416,16 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) {
</Box> </Box>
{CancelButton()} {CancelButton()}
</Box> </Box>
<Snackbar anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} open={open} onClose={handleClose}> <Snackbar
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
open={open}
onClose={handleClose}
>
<Alert <Alert
onClose={handleClose} onClose={handleClose}
severity={info?.type} severity={info?.type}
variant="filled" variant="filled"
sx={{ width: '100%' }} sx={{ width: "100%" }}
> >
{info?.message} {info?.message}
</Alert> </Alert>

View File

@ -69,7 +69,6 @@ export const HomeWrapper = styled(Box)({
export const TabsContainer = styled(Box)({ export const TabsContainer = styled(Box)({
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
gap: "15px",
alignItems: "flex-start", alignItems: "flex-start",
width: "100%", width: "100%",
justifyContent: "center", justifyContent: "center",
@ -78,6 +77,7 @@ export const TabsContainer = styled(Box)({
export const TabsRow = styled(Box)({ export const TabsRow = styled(Box)({
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "row",
gap: "5px",
justifyContent: "space-evenly", justifyContent: "space-evenly",
alignItems: "center", alignItems: "center",
backgroundColor: "#323336", backgroundColor: "#323336",

View File

@ -58,17 +58,17 @@ export const HomePage = () => {
<Tab activeTab={mode === "buy"} onClick={() => setMode("buy")}> <Tab activeTab={mode === "buy"} onClick={() => setMode("buy")}>
Buy QORT Buy QORT
</Tab> </Tab>
<TabDivider activeTab={mode === "buy" || mode === "sell"} /> {/* <TabDivider activeTab={mode === "buy" || mode === "sell"} /> */}
<Tab activeTab={mode === "sell"} onClick={() => setMode("sell")}> <Tab activeTab={mode === "sell"} onClick={() => setMode("sell")}>
Sell QORT Sell QORT
</Tab> </Tab>
<TabDivider activeTab={mode === "sell" || mode === "history"} /> {/* <TabDivider activeTab={mode === "sell" || mode === "history"} /> */}
<Tab {/* <Tab
activeTab={mode === "history"} activeTab={mode === "history"}
onClick={() => setMode("history")} onClick={() => setMode("history")}
> >
Trade History Trade History
</Tab> </Tab> */}
</TabsRow> </TabsRow>
</TabsContainer> </TabsContainer>
<div <div