From a2c773b301ba7a7e3664cd2258fe0ebdf2203998 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 23 Nov 2024 13:34:46 +0200 Subject: [PATCH 01/25] change logo --- index.html | 6 ++-- .../DbComponents/OngoingTransactions.tsx | 2 +- src/components/Grids/TradeOffers.tsx | 31 +++++++++++++----- src/components/Terms.tsx | 8 ++--- src/components/common/icons/qtradeLogo.png | Bin 0 -> 5739 bytes src/components/header/Header.tsx | 10 +++--- 6 files changed, 35 insertions(+), 22 deletions(-) create mode 100644 src/components/common/icons/qtradeLogo.png diff --git a/index.html b/index.html index 0ec4677..a31d64f 100644 --- a/index.html +++ b/index.html @@ -2,9 +2,7 @@ - + @@ -14,7 +12,7 @@ - Qort.Trade + Q-Trade
diff --git a/src/components/DbComponents/OngoingTransactions.tsx b/src/components/DbComponents/OngoingTransactions.tsx index 566393a..4430e64 100644 --- a/src/components/DbComponents/OngoingTransactions.tsx +++ b/src/components/DbComponents/OngoingTransactions.tsx @@ -5,7 +5,7 @@ import { useIndexedDBContext } from "../../contexts/indexedDBContext"; const fetchTradeInfo = async (qortalAtAddress) => { - const checkIfOfferingRes = await fetch(`http://127.0.0.1:12391/crosschain/trade/${qortalAtAddress}`) + const checkIfOfferingRes = await fetch(`/crosschain/trade/${qortalAtAddress}`) const data = await checkIfOfferingRes.json() return data }; diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index 03c4e52..bd9797f 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -362,7 +362,11 @@ export const TradeOffers: React.FC = ({ltcBalance}:any) => { // Update transactions in IndexedDB const result = await updateTransactionInDB(transactionData); - + setOpen(true) + setInfo({ + type: 'success', + message: "Submitted Order" + }) fetchOngoingTransactions() if(isUsingGateway){ setRecord(transactionData) @@ -370,11 +374,7 @@ export const TradeOffers: React.FC = ({ltcBalance}:any) => { message: `Keep a record of your order in case your trade gets stuck`, }) } - setOpen(true) - setInfo({ - type: 'success', - message: "Submitted Order" - }) + } } catch (error) { @@ -554,11 +554,24 @@ const handleClose = ( > {"Download record"} - + {messageInfo.message} - diff --git a/src/components/Terms.tsx b/src/components/Terms.tsx index d1e31a4..ddba254 100644 --- a/src/components/Terms.tsx +++ b/src/components/Terms.tsx @@ -55,10 +55,10 @@ export const Terms =() => { - The purpose of qort.trade is to make trading LTC for QORT as easy as possible. The maintainers of this site do not profit from its use—there are no additional fees for buying QORT through this site. There are two ways to place a buy order: + The purpose of q-trade is to make trading LTC for QORT as easy as possible. The maintainers of this site do not profit from its use—there are no additional fees for buying QORT through this site. There are two ways to place a buy order: 1. Use the gateway 2. Use your local node. - By using qort.trade, you agree to the following terms and conditions. + By using q-trade, you agree to the following terms and conditions. @@ -66,11 +66,11 @@ export const Terms =() => { - If you are uncomfortable using the gateway, we offer the option to use your local node to buy QORT. When logging into the extension, choose the local node configuration, and use the switch button on qort.trade to connect with your local node. + If you are uncomfortable using the gateway, we offer the option to use your local node to buy QORT. When logging into the UI, choose the local node configuration. - The maintainers of this site are not responsible for any lost LTC, QORT, or other cryptocurrencies that may result from using this site. This is a hobby project, and mistakes in the code may occur. Please proceed with caution. + The maintainers and devs of this site are not responsible for any lost LTC, QORT, or other cryptocurrencies that may result from using this site. This is a hobby project, and mistakes in the code may occur. Please proceed with caution. diff --git a/src/components/common/icons/qtradeLogo.png b/src/components/common/icons/qtradeLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..2735d38a2431caf8caf679dec21882f31e1b7d98 GIT binary patch literal 5739 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 { > @@ -190,7 +190,9 @@ export const Header = ({ qortBalance, ltcBalance, mode, setMode }: any) => { - + Balance: {qortBalance} QORT |{" "} {ltcBalance === null ? "N/A" : ltcBalance} LTC From 160ce1cf21915cbce49cbd41fdfe54743f122f34 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 23 Dec 2024 08:44:07 +0200 Subject: [PATCH 02/25] added other coins --- src/App.tsx | 64 +++++++-- src/assets/img/arrr.png | Bin 0 -> 1429 bytes src/assets/img/btc.png | Bin 0 -> 1982 bytes src/assets/img/dgb.png | Bin 0 -> 4917 bytes src/assets/img/doge.png | Bin 0 -> 1798 bytes src/assets/img/ltc.png | Bin 0 -> 1480 bytes src/assets/img/qort.png | Bin 0 -> 1907 bytes src/assets/img/rvn.png | Bin 0 -> 2405 bytes src/components/Grids/OngoingTrades.tsx | 34 +++-- src/components/Grids/TradeOffers.tsx | 142 ++++++++++++++----- src/components/Terms.tsx | 6 +- src/components/header/Header.tsx | 76 ++++++++++- src/components/sell/CreateSell.tsx | 40 ++++-- src/components/sell/TradeBotList.tsx | 181 +++++++++++++++++-------- src/contexts/gameContext.ts | 12 +- src/pages/Home/Home.tsx | 6 +- 16 files changed, 425 insertions(+), 136 deletions(-) create mode 100644 src/assets/img/arrr.png create mode 100644 src/assets/img/btc.png create mode 100644 src/assets/img/dgb.png create mode 100644 src/assets/img/doge.png create mode 100644 src/assets/img/ltc.png create mode 100644 src/assets/img/qort.png create mode 100644 src/assets/img/rvn.png diff --git a/src/App.tsx b/src/App.tsx index 46c2081..2fb3bd8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import ReactGA from "react-ga4"; import "./App.css"; import socketService from "./services/socketService"; @@ -71,7 +71,12 @@ export async function sendRequestToExtension( function App() { const [userInfo, setUserInfo] = useState(null); const [qortBalance, setQortBalance] = useState(null); - const [ltcBalance, setLtcBalance] = useState(null); + const [balances, setBalances] = useState({}); + const [selectedCoin, setSelectedCoin] = useState("LITECOIN"); + + const foreignCoinBalance = useMemo(()=> { + return balances[selectedCoin] || null + }, [balances, selectedCoin]) const [isAuthenticated, setIsAuthenticated] = useState(false); const [OAuthLoading, setOAuthLoading] = useState(false); const db = useIndexedDBContext(); @@ -172,14 +177,19 @@ function App() { setQortBalance(balanceResponse.data?.value) } - const getLTCBalance = async () => { + const getLTCBalance = async (coin) => { try { const response = await qortalRequest({ action: "GET_WALLET_BALANCE", - coin: "LTC" + coin: getCoinLabel(coin) }); if(!response?.error){ - setLtcBalance(+response) + setBalances((prev)=> { + return { + ...prev, + [coin]: +response + } + }) } } catch (error) { // @@ -187,18 +197,18 @@ function App() { } useEffect(() => { - if(!userInfo?.address) return + if(!userInfo?.address || !selectedCoin) return const intervalGetTradeInfo = setInterval(() => { fetchOngoingTransactions() - getLTCBalance() + getLTCBalance(selectedCoin) getQortBalance() }, 150000) - getLTCBalance() + getLTCBalance(selectedCoin) getQortBalance() return () => { clearInterval(intervalGetTradeInfo) } - }, [userInfo?.address, isAuthenticated]) + }, [userInfo?.address, isAuthenticated, selectedCoin]) const handleMessage = async (event: any) => { @@ -208,7 +218,6 @@ function App() { setAvatar(""); setIsAuthenticated(false); setQortBalance(null) - setLtcBalance(null) localStorage.setItem("token", ""); } else if(event.data.type === "RESPONSE_FOR_TRADES"){ @@ -246,6 +255,37 @@ function App() { }; }, [userInfo?.address]); + const getCoinLabel = (coin?: string)=> { + switch(coin || selectedCoin){ + case "LITECOIN":{ + + return 'LTC' + } + case "DOGECOIN":{ + + return 'DOGE' + } + case "BITCOIN":{ + + return 'BTC' + } + case "DIGIBYTE":{ + + return 'DGB' + } + case "RAVENCOIN":{ + + return 'RVN' + } + case "PIRATECHAIN":{ + + return 'ARRR' + } + default: + return null + } + } + const gameContextValue: IContextProps = { userInfo, setUserInfo, @@ -253,7 +293,7 @@ function App() { setUserNameAvatar, onGoingTrades, fetchOngoingTransactions, - ltcBalance, + foreignCoinBalance, qortBalance, isAuthenticated, setIsAuthenticated, @@ -261,7 +301,7 @@ function App() { setOAuthLoading, updateTransactionInDB, sellOrders, - deleteTemporarySellOrder, updateTemporaryFailedTradeBots, fetchTemporarySellOrders, isUsingGateway + deleteTemporarySellOrder, updateTemporaryFailedTradeBots, fetchTemporarySellOrders, isUsingGateway, selectedCoin, setSelectedCoin, getCoinLabel }; diff --git a/src/assets/img/arrr.png b/src/assets/img/arrr.png new file mode 100644 index 0000000000000000000000000000000000000000..d27456576e737c1353e256810d9b84dce7b42784 GIT binary patch literal 1429 zcmV;G1#0?k}ogVt_iv;g(_nl!MG8$U!DoWdu9kkw6h-ftOt@TB<<3sK5#b z%0a@oI37WePGIvKtR6|AI9mhAv#hf;g!DQH`61I`vLh+0A4)5ZAgMw_(fIw3) zJ;zl&sHJKOrdX)Hx;#!mMwh{?4+1iNCMv%cs&_F=hEbm`q|2jkHpr9){rIV#T^^%{ zqGI`FG|T-NsX|E0@RAuy6F+A}zL1-lD6p(ZBB$8#sMK3e0-2 zyN`|tb@-8)tA&n=Zp7vthfK?qt?H*&?8Ec>6y1K*Zl$YZC?)`2i2k;rbN5(Ja(f&> z+I84U^Cc*)6S}%Am?43*v&+{5%?Ss(|8(3zHn20hJXOrjyFuJRHpKXrg=S^dV)^SF z2l1dSSLXN6@BBXR1AV4vV;|eVi@+ZM7|I3uWBCi4oxV;L$+&{tePqNO-{FT843s4> zt0X1g`RVPW&|um@5()C9DX`ECi?KogX;&s8bNr+h1Qa2GTYhodULx1%dy;)-VmsA8APs0umM;p4i-Xy#(i%@Nrq=){EG``n z8)S$XAXhDYofCb_5&*4s8%WbSm%jwg^81isSt#ifaMl(afEg7*a+^RVP@R{Xlpw?2 z%hXKPIu?O+%R)&sbZ#P2AVGhAeK+?v5vio%PTVq$g+>dewJ1n1c7n>l((*&0+sF37 z1hC4FuZp}(QZ5SWcE=w6xh%tCDC7HY^JVk?3&ug07?jk#AM% zp8|1O^(-h(zSY1=b8vN(m9}V|ZWap4hn1x0U^z0D!f$4jecbWOsfvh5d+Q34B;Czhy%(n6vZ9lYt6&UXDakYjKP`=$833&u30U=V6#}s8^*@Ag)eEW!;kNq< jH3JJkl>Q2}zXA*Z%L07o@pf>Q00000NkvXXu0mjfhXs}` literal 0 HcmV?d00001 diff --git a/src/assets/img/btc.png b/src/assets/img/btc.png new file mode 100644 index 0000000000000000000000000000000000000000..fe3fd1a030bd2736ed76748d27e6584b3fa05c3a GIT binary patch literal 1982 zcmV;v2SNCWP)htPv-zHc{0_qe;RVIF+Z!7aDH+~!cJ9zWE z0z3R;&-88FK?e2QLmp!@E8cvh&hc=%{kbT8{6vWNHYd)RK99@$W6Yf$C}t1aw#Jzg z0V;Sa!^E?jk>}QSm5SwX6l)KBQTk+0IO|oeTiQv=s9ZvBZmmTrZ`ziI&lxG$;Z&v6ZUQj_sWG~M))W^e7D$CuHznMHr>Q@{nSfDQWq}b2MbIgTT zz^IxC6f5m)AyTYIo(7ztPO6RN45fh8bmc!vp{zk(9*5z6*bt5knS!?+5r9(7+s`Og z?bu8S)%ld|@)Abn>wnn39_;7E^Qti19)(pp?UZ&|FD+GddH97GzI-gMPL6m?%H8Hf z;BTPqMk*8w0Dl_&ZDq&7%d6~1XOj+Y*lnpJ?8F$`y-}IZHKxuO#X?@LytmFKZ7cjp zm4`i*Ihj;x7-&P-YUihk;4^t|1PduCvl(bi0LA>&TAed0Bbo?>P7amrmG>zweyU3w zysJEYn(tq0utMD!<@h6odbVIvDn(&+f_WHr_a)eS%A;Q$({d2!R!ywkXPrCd6#^(F zE{%&VAxye_@kptbl5T=h8B2ZFK(TUCE2=^P6o8w1j#TaCuT9$ZYHYq2>ZO~Y(7LeL zlI$ln^MPu<3X>9tK5KXD?jqUG#B*OK+el}f^+}HBMpaA}!G0FB)vW^6VL#I<9!vvc zvui_j+0SthPjEQFzdcrkmTd*}6g5Giqr6@n7oAlwe?FK!4{E5NwMc{tc%(KO0l2AS2uN1C~N>&=eDPwE0M1Q&!51npP5^&=$w5ojIT|E z`sCapF9DqY_0 z+J|C!i9BebTjc0EMs#dhkT|H*HdVFr3qr0yEoCT-l&?ra>O#pQXH}u1CnGm{ZR#upDJd zAPX%9211iOc{)tQg^rKrimI7*G0K8b0H%eSJuz4R{c!3Df9z-(?jPmPNWh7eUc%AtWtvpeHm= z)d4#RUVIebD|nSnlKm`D8_XNQu|T16f-CC|w8w@32%HSnX%!3T+1sU?sB+R+~g}N)wvhE_5HY3Wvi3>K5JK!&P>kH!4c@j{x84Y3h8GeDB zP=mU;AbI8p7680=3~K8A?@~?0dB>pgZzebeb(w8yKMPESo@ zz}%N0bFE#}vP(EFZElOj-3oJn8Bp#5s+Reocq3Jgf{KYk2i&`^_lB%q3{cb;14Mv@ zoOe&GUR}WgcJ*=<2C%Ew|1iQ`y`YK_Zuk{yRXozXLhTek0iyL+sQoX%0DJn9yuv^) QjsO4v07*qoM6N<$g5oNn-~a#s literal 0 HcmV?d00001 diff --git a/src/assets/img/dgb.png b/src/assets/img/dgb.png new file mode 100644 index 0000000000000000000000000000000000000000..6950158b56d73dcce67b8fece5cc1d1817b6b51c GIT binary patch literal 4917 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 literal 0 HcmV?d00001 diff --git a/src/assets/img/doge.png b/src/assets/img/doge.png new file mode 100644 index 0000000000000000000000000000000000000000..d99fa2ff2a6b1c969c23a22d9d265fa2c872a9ac GIT binary patch literal 1798 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 Xq@DHT-WO?J00000NkvXXu0mjfNJg50 literal 0 HcmV?d00001 diff --git a/src/components/Grids/OngoingTrades.tsx b/src/components/Grids/OngoingTrades.tsx index 5a62d42..b3f93a4 100644 --- a/src/components/Grids/OngoingTrades.tsx +++ b/src/components/Grids/OngoingTrades.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'; import { AgGridReact } from 'ag-grid-react'; -import { ColDef, SizeColumnsToContentStrategy } from 'ag-grid-community'; +import { ColDef, RowClassParams, RowStyle, SizeColumnsToContentStrategy } from 'ag-grid-community'; import 'ag-grid-community/styles/ag-grid.css'; import 'ag-grid-community/styles/ag-theme-alpine.css'; import gameContext from '../../contexts/gameContext'; @@ -10,9 +10,17 @@ const autoSizeStrategy: SizeColumnsToContentStrategy = { }; export const OngoingTrades = () => { - const { onGoingTrades } = useContext(gameContext); - + const { onGoingTrades, getCoinLabel, selectedCoin } = useContext(gameContext); + const gridRef = useRef(null) + + + const onGridReady = useCallback((params: any) => { + // params.api.sizeColumnsToFit(); // Adjust columns to fit the grid width + // const allColumnIds = params.columnApi.getAllColumns().map((col: any) => col.getColId()); + // params.columnApi.autoSizeColumns(allColumnIds); // Automatically adjust the width to fit content + }, []); + const defaultColDef = { resizable: true, // Make columns resizable by default sortable: true, // Make columns sortable by default @@ -35,9 +43,10 @@ export const OngoingTrades = () => { resizable: true , flex: 1, minWidth: 100 }, - { headerName: "Amount (QORT)", valueGetter: (params) => +params.data.tradeInfo.qortAmount, resizable: true, flex: 1, minWidth: 100 }, - { headerName: "LTC/QORT", valueGetter: (params) => +params.data.tradeInfo.expectedForeignAmount / +params.data.tradeInfo.qortAmount , resizable: true , flex: 1, minWidth: 100}, - { headerName: "Total LTC Value", valueGetter: (params) => +params.data.tradeInfo.expectedForeignAmount, resizable: true , flex: 1, minWidth: 100 }, + { headerName: "Amount (QORT)", valueGetter: (params) => +params.data.tradeInfo.qortAmount, resizable: true, flex: 1, minWidth: 150 }, + { headerName: `${getCoinLabel()}/QORT`, valueGetter: (params) => +params.data.tradeInfo.expectedForeignAmount / +params.data.tradeInfo.qortAmount , resizable: true , flex: 1, minWidth: 150}, + { headerName: `Total ${getCoinLabel()} Value`, valueGetter: (params) => +params.data.tradeInfo.expectedForeignAmount, resizable: true , flex: 1, minWidth: 150, + }, { headerName: "Notes", valueGetter: (params) => { if (params.data.tradeInfo.mode === 'TRADING') { @@ -54,7 +63,7 @@ export const OngoingTrades = () => { } if (params.data.message) return params.data.message - }, resizable: true, flex: 1, minWidth: 100 + }, resizable: true, flex: 1, minWidth:300, autoHeight: true, cellStyle: { whiteSpace: 'normal', wordBreak: 'break-word', padding: '5px' }, } ]; @@ -65,15 +74,20 @@ export const OngoingTrades = () => { // return null; // }; const getRowId = useCallback(function (params: any) { - return String(params.data._id); + return String(params.data?.qortalAtAddress); }, []); + + return (
item?.tradeInfo?.foreignBlockchain === selectedCoin)} // onRowClicked={onRowClicked} rowSelection="single" getRowId={getRowId} @@ -85,7 +99,7 @@ export const OngoingTrades = () => { // domLayout='autoHeight' // getRowStyle={getRowStyle} - + />
); diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index bd9797f..bdd5721 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -11,6 +11,9 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useModal } from '../common/useModal'; import FileSaver from 'file-saver'; +// export const baseLocalHost = window.location.host +export const baseLocalHost = '127.0.0.1:12391' + interface RowData { amountQORT: number; priceUSD: number; @@ -32,10 +35,10 @@ export const autoSizeStrategy: SizeColumnsToContentStrategy = { type: 'fitCellContents' }; -export const TradeOffers: React.FC = ({ltcBalance}:any) => { +export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { const [offers, setOffers] = useState([]) - - const { fetchOngoingTransactions, onGoingTrades, updateTransactionInDB, isUsingGateway } = useContext(gameContext); + const [qortalNames, setQortalNames] = useState({}) + const { fetchOngoingTransactions, onGoingTrades, updateTransactionInDB, isUsingGateway, getCoinLabel, selectedCoin } = useContext(gameContext); const listOfOngoingTradesAts = useMemo(()=> { return onGoingTrades?.filter((item)=> item?.status !== 'trade-failed')?.map((trade)=> trade?.qortalAtAddress) || [] }, [onGoingTrades]) @@ -50,7 +53,7 @@ export const TradeOffers: React.FC = ({ltcBalance}:any) => { const offersWithoutOngoing = useMemo(()=> { return offers.filter((item)=> !listOfOngoingTradesAts.includes(item.qortalAtAddress)) }, [listOfOngoingTradesAts, offers]) - + const initiatedFetchPresence = useRef(false) const [selectedOffer, setSelectedOffer] = useState(null) @@ -80,6 +83,30 @@ export const TradeOffers: React.FC = ({ltcBalance}:any) => { suppressMovable: true, // Prevent columns from being movable }; + const getName = async (address)=> { + try { + const response = await fetch("/names/address/" + address); + const nameData = await response.json(); + if (nameData?.length > 0) { + setQortalNames((prev)=> { + return { + ...prev, + [address]: nameData[0].name + } + }) + } else { + setQortalNames((prev)=> { + return { + ...prev, + [address]: null + } + }) + } + } catch (error) { + // error + } + } + const columnDefs: ColDef[] = [ { headerCheckboxSelection: true, // Adds a checkbox in the header for selecting all rows @@ -92,15 +119,28 @@ export const TradeOffers: React.FC = ({ltcBalance}:any) => { { headerName: "QORT AMOUNT", field: "qortAmount" , flex: 1, // Flex makes this column responsive minWidth: 150, // Ensure it doesn't shrink too much resizable: true }, - { headerName: "LTC/QORT", valueGetter: (params) => +params.data.foreignAmount / +params.data.qortAmount, sortable: true, sort: 'asc', flex: 1, // Flex makes this column responsive + { headerName: `${getCoinLabel()}/QORT`, valueGetter: (params) => +params.data.foreignAmount / +params.data.qortAmount, sortable: true, sort: 'asc', flex: 1, // Flex makes this column responsive minWidth: 150, // Ensure it doesn't shrink too much resizable: true }, - { headerName: "Total LTC Value", field: "foreignAmount", flex: 1, // Flex makes this column responsive + { headerName: `Total ${getCoinLabel()} Value`, field: "foreignAmount", flex: 1, // Flex makes this column responsive minWidth: 150, // Ensure it doesn't shrink too much resizable: true }, { headerName: "Seller", field: "qortalCreator", flex: 1, // Flex makes this column responsive minWidth: 300, // Ensure it doesn't shrink too much - resizable: true }, + resizable: true, valueGetter: (params)=> { + if(params?.data?.qortalCreator){ + if(qortalNames[params?.data?.qortalCreator]){ + return qortalNames[params?.data?.qortalCreator] + } else if(qortalNames[params?.data?.qortalCreator] === undefined){ + getName(params?.data?.qortalCreator) + + return params?.data?.qortalCreator + } else { + return params?.data?.qortalCreator + + } + } + } }, ]; @@ -225,7 +265,7 @@ export const TradeOffers: React.FC = ({ltcBalance}:any) => { if(isUsingGateway){ socketLink = `wss://appnode.qortal.org/websockets/crosschain/tradepresence` } else { - socketLink = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/websockets/crosschain/tradepresence`; + socketLink = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${baseLocalHost}/websockets/crosschain/tradepresence`; } @@ -245,44 +285,59 @@ export const TradeOffers: React.FC = ({ltcBalance}:any) => { } socket.onerror = (e) => { clearTimeout(socketTimeout) + restartTradePresenceWebSocket() } const pingSocket = () => { socket.send('ping') socketTimeout = setTimeout(pingSocket, 295000) } } + const socketRef = useRef(null) + + const restartTradeOffers = ()=> { + if (socketRef.current) { + socketRef.current.close(1000, 'forced'); // Close with a custom reason + socketRef.current = null + } + offeringTrades.current = [] + setOffers([]) + setSelectedOffer(null) + } const initTradeOffersWebSocket = (restarted = false) => { - let tradeOffersSocketCounter = 0 + + if(socketRef.current) return let socketTimeout: any let socketLink if(isUsingGateway){ - socketLink = `wss://appnode.qortal.org/websockets/crosschain/tradeoffers?foreignBlockchain=LITECOIN&includeHistoric=true` + socketLink = `wss://appnode.qortal.org/websockets/crosschain/tradeoffers?foreignBlockchain=${selectedCoin}&includeHistoric=true` } else { - socketLink = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/websockets/crosschain/tradeoffers?foreignBlockchain=LITECOIN&includeHistoric=true` + socketLink = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${baseLocalHost}/websockets/crosschain/tradeoffers?foreignBlockchain=${selectedCoin}&includeHistoric=true` } - const socket = new WebSocket(socketLink) - socket.onopen = () => { + socketRef.current = new WebSocket(socketLink) + socketRef.current.onopen = () => { setTimeout(pingSocket, 50) - tradeOffersSocketCounter += 1 } - socket.onmessage = (e) => { - offeringTrades.current = [...offeringTrades.current, ...JSON.parse(e.data)] - tradeOffersSocketCounter += 1 + socketRef.current.onmessage = (e) => { + offeringTrades.current = [...offeringTrades.current?.filter((coin)=> coin?.foreignBlockchain === selectedCoin), ...JSON.parse(e.data)?.filter((coin)=> coin?.foreignBlockchain === selectedCoin)] restarted = false processOffersWithPresence() } - socket.onclose = () => { + socketRef.current.onclose = (event) => { clearTimeout(socketTimeout) + if (event.reason === 'forced') { + return + } restartTradeOffersWebSocket() + socketRef.current = null } - socket.onerror = (e) => { + socketRef.current.onerror = (e) => { clearTimeout(socketTimeout) } const pingSocket = () => { - socket.send('ping') + socketRef.current.send('ping') socketTimeout = setTimeout(pingSocket, 295000) } } @@ -290,8 +345,11 @@ export const TradeOffers: React.FC = ({ltcBalance}:any) => { useEffect(() => { blockedTradesList.current = JSON.parse(localStorage.getItem('failedTrades') || '[]') - initTradePresenceWebSocket() - initTradeOffersWebSocket() + if(!initiatedFetchPresence.current){ + initiatedFetchPresence.current = true + initTradePresenceWebSocket() + + } getNewBlockedTrades() const intervalBlockTrades = setInterval(() => { getNewBlockedTrades() @@ -302,6 +360,24 @@ export const TradeOffers: React.FC = ({ltcBalance}:any) => { } }, [isUsingGateway]) + + + useEffect(() => { + if(selectedCoin === null) return + restartTradeOffers() + setTimeout(() => { + initTradeOffersWebSocket() + + }, 500); + return () => { + if(socketRef.current){ + socketRef.current.close(1000, 'forced'); + } + } + }, [isUsingGateway, selectedCoin]) + + + const selectedTotalLTC = useMemo(() => { @@ -313,11 +389,11 @@ export const TradeOffers: React.FC = ({ltcBalance}:any) => { const buyOrder = async () => { try { - if(+ltcBalance < +selectedTotalLTC.toFixed(4)){ + if(+foreignCoinBalance < +selectedTotalLTC.toFixed(4)){ setOpen(true) setInfo({ type: 'error', - message: "You don't have enough LTC or your balance was not retrieved" + message: `You don't have enough ${getCoinLabel()} or your balance was not retrieved` }) return } @@ -329,11 +405,10 @@ export const TradeOffers: React.FC = ({ltcBalance}:any) => { message: "Attempting to submit buy order. Please wait..." }) const listOfATs = selectedOffers - const response = await qortalRequestWithTimeout({ action: "CREATE_TRADE_BUY_ORDER", crosschainAtInfo: listOfATs, - foreignBlockchain: 'LITECOIN' + foreignBlockchain: selectedCoin }, 900000); if(response?.error){ @@ -381,7 +456,7 @@ export const TradeOffers: React.FC = ({ltcBalance}:any) => { setOpen(true) setInfo({ type: 'error', - message: error?.message || "Failed to submit trade order." + message: error?.error || error?.message || "Failed to submit trade order." }) console.error(error) } @@ -471,7 +546,7 @@ const handleClose = ( onSelectionChanged={onSelectionChanged} getRowStyle={getRowStyle} autoSizeStrategy={autoSizeStrategy} - rowSelection="multiple" // Enable multi-select + rowSelection={selectedCoin === 'PIRATECHAIN' ? "single" :"multiple"} // Enable multi-select rowMultiSelectWithClick={true} suppressHorizontalScroll={false} // Allow horizontal scroll on mobile if needed suppressCellFocus={true} // Prevents cells from stealing focus in mobile @@ -486,6 +561,9 @@ const handleClose = ( )} */} +
ltcBalance ? 'red' : 'white', + color: selectedTotalLTC > foreignCoinBalance ? 'red' : 'white', }}>{selectedTotalLTC?.toFixed(4)} LTC + }}>{`${getCoinLabel()} `} @@ -528,9 +606,9 @@ const handleClose = ( fontSize: '16px', color: 'white', - }}>{ltcBalance?.toFixed(4)} {foreignCoinBalance?.toFixed(4)} LTC balance + }}>{`${getCoinLabel()} `} balance {BuyButton()} diff --git a/src/components/Terms.tsx b/src/components/Terms.tsx index ddba254..3ab87f2 100644 --- a/src/components/Terms.tsx +++ b/src/components/Terms.tsx @@ -55,14 +55,14 @@ export const Terms =() => { - The purpose of q-trade is to make trading LTC for QORT as easy as possible. The maintainers of this site do not profit from its use—there are no additional fees for buying QORT through this site. There are two ways to place a buy order: + The purpose of q-trade is to make trading LTC and other coins for QORT as easy as possible. The maintainers of this site do not profit from its use—there are no additional fees for buying QORT through this site. There are two ways to place a buy order: 1. Use the gateway 2. Use your local node. By using q-trade, you agree to the following terms and conditions. - Using the gateway means you trust the maintainer of the node, as your LTC private key will need to be handled by that node to execute a trade order. If you have more than 4 QORT and your public key is already on the blockchain, your LTC private key will be transmitted using q-chat. If not, the message will be encrypted in the same manner as q-chat but stored temporarily in a database to ensure it reaches its destination. + Using the gateway means you trust the maintainer of the node, as your foreign coin (i.e. LTC) private key will need to be handled by that node to execute a trade order. If you have more than 4 QORT and your public key is already on the blockchain, your foreign coin private key will be transmitted using q-chat. If not, the message will be encrypted in the same manner as q-chat but stored temporarily in a database to ensure it reaches its destination. @@ -70,7 +70,7 @@ export const Terms =() => { - The maintainers and devs of this site are not responsible for any lost LTC, QORT, or other cryptocurrencies that may result from using this site. This is a hobby project, and mistakes in the code may occur. Please proceed with caution. + The maintainers and devs of this site are not responsible for any lost foreign coin, QORT, or other cryptocurrencies that may result from using this site. This is a hobby project, and mistakes in the code may occur. diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index c74b2e5..7f5b829 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -36,6 +36,12 @@ import { } from "@mui/material"; import { sendRequestToExtension } from "../../App"; import { Terms } from "../Terms"; +import ltcIcon from '../../assets/img/ltc.png' +import btcIcon from '../../assets/img/btc.png' +import dogeIcon from '../../assets/img/doge.png' +import rvnIcon from '../../assets/img/rvn.png' +import dgbIcon from '../../assets/img/dgb.png' +import arrrIcon from '../../assets/img/arrr.png' const checkIfLocal = async () => { try { @@ -59,7 +65,47 @@ export const Label = styled("label")( ` ); -export const Header = ({ qortBalance, ltcBalance, mode, setMode }: any) => { +const SelectRow = ({coin})=> { + let img + switch (coin) { + case 'LTC': + img = ltcIcon + break; + case 'BTC': + img = btcIcon + break; + + case 'DOGE': + img = dogeIcon + break; + case 'RVN': + img = rvnIcon + break; + + case 'ARRR': + img = arrrIcon + break; + case 'DGB': + img = dgbIcon + break; + default: + null + } + + return ( +

{coin}

+ ) +} + +export const Header = ({ qortBalance, foreignCoinBalance, mode, setMode }: any) => { const [openDropdown, setOpenDropdown] = useState(false); const dropdownRef = useRef(null); const buttonRef = useRef(null); @@ -67,7 +113,6 @@ export const Header = ({ qortBalance, ltcBalance, mode, setMode }: any) => { const [open, setOpen] = useState(false); const [info, setInfo] = useState(null); const { isUsingGateway } = useContext(gameContext); - const [selectedCoin, setSelectedCoin] = useState("LITECOIN"); const handleChange = (event: ChangeEvent) => { setChecked(false); @@ -77,7 +122,7 @@ export const Header = ({ qortBalance, ltcBalance, mode, setMode }: any) => { message: "Change the node you are using at the authentication page", }); }; - const { userInfo } = useContext(gameContext); + const { userInfo, selectedCoin, setSelectedCoin, getCoinLabel } = useContext(gameContext); const { avatar, setAvatar } = useContext(UserContext); const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ @@ -185,7 +230,12 @@ export const Header = ({ qortBalance, ltcBalance, mode, setMode }: any) => { value={selectedCoin} onChange={(e) => setSelectedCoin(e.target.value)} > - LTC + + + + + + @@ -195,7 +245,7 @@ export const Header = ({ qortBalance, ltcBalance, mode, setMode }: any) => { }}> Balance: {qortBalance} QORT |{" "} - {ltcBalance === null ? "N/A" : ltcBalance} LTC + {foreignCoinBalance === null ? "N/A" : foreignCoinBalance} {getCoinLabel()} {userInfo?.name ? ( @@ -247,8 +297,20 @@ export const Header = ({ qortBalance, ltcBalance, mode, setMode }: any) => { alignItems: "center", }} > - - + + { const [open, setOpen] = React.useState(false); const [qortAmount, setQortAmount] = React.useState(0) const [foreignAmount, setForeignAmount] = React.useState(0) - const {updateTemporaryFailedTradeBots, sellOrders, fetchTemporarySellOrders, isUsingGateway} = useContext(gameContext) + const {updateTemporaryFailedTradeBots, sellOrders, fetchTemporarySellOrders, isUsingGateway, getCoinLabel, selectedCoin} = useContext(gameContext) const [openAlert, setOpenAlert] = React.useState(false) const [info, setInfo] = React.useState(null) const handleClickOpen = () => { @@ -80,7 +100,6 @@ export const CreateSell = ({qortAddress, show}) => { const createSellOrder = async() => { try { - setOpen(true) setInfo({ type: 'info', message: "Attempting to create sell order. Please wait..." @@ -88,8 +107,8 @@ export const CreateSell = ({qortAddress, show}) => { const res = await qortalRequestWithTimeout({ action: "CREATE_TRADE_SELL_ORDER", qortAmount, - foreignBlockchain: 'LITECOIN', - foreignAmount + foreignBlockchain: selectedCoin, + foreignAmount: qortAmount * foreignAmount }, 900000); if(res?.error && res?.failedTradeBot){ @@ -167,7 +186,6 @@ export const CreateSell = ({qortAddress, show}) => {
) } - return (
{ }} > - New Sell Order - QORT for LTC + {`New Sell Order - QORT for ${getCoinLabel()}`} { /> - Price Each (LTC) + {`Price Each (${getCoinLabel()})`} { autoComplete="off" /> - {qortAmount * foreignAmount} LTC for {qortAmount} QORT + {`${qortAmount * foreignAmount} ${getCoinLabel()}`} for {qortAmount} QORT Total sell amount needs to be greater than: {minimumAmountSellTrades.LITECOIN.value} {' '} {minimumAmountSellTrades.LITECOIN.ticker} + }}>Total sell amount needs to be greater than: {minimumAmountSellTrades[selectedCoin]?.value} {' '} {minimumAmountSellTrades[selectedCoin]?.ticker} @@ -236,7 +254,7 @@ export const CreateSell = ({qortAddress, show}) => { - diff --git a/src/components/sell/TradeBotList.tsx b/src/components/sell/TradeBotList.tsx index 8bc86ff..c317868 100644 --- a/src/components/sell/TradeBotList.tsx +++ b/src/components/sell/TradeBotList.tsx @@ -8,7 +8,7 @@ import React, { useRef, useState, } from "react"; -import { autoSizeStrategy } from "../Grids/TradeOffers"; +import { autoSizeStrategy, baseLocalHost } from "../Grids/TradeOffers"; import { Alert, Box, Snackbar, SnackbarCloseReason, Typography } from "@mui/material"; import gameContext from "../../contexts/gameContext"; @@ -18,47 +18,47 @@ const defaultColDef = { suppressMovable: true, // Prevent columns from being movable }; -const columnDefs: ColDef[] = [ - { - headerCheckboxSelection: false, // Adds a checkbox in the header for selecting all rows - checkboxSelection: true, // Adds checkboxes in each row for selection - headerName: "Select", // You can customize the header name - width: 50, // Adjust the width as needed - pinned: "left", // Optional, to pin this column on the left - resizable: false, - }, - { - headerName: "QORT AMOUNT", - field: "qortAmount", - flex: 1, // Flex makes this column responsive - minWidth: 150, // Ensure it doesn't shrink too much - resizable: true, - }, - { - headerName: "LTC/QORT", - valueGetter: (params) => - +params.data.foreignAmount / +params.data.qortAmount, - sortable: true, - sort: "asc", - flex: 1, // Flex makes this column responsive - minWidth: 150, // Ensure it doesn't shrink too much - resizable: true, - }, - { - headerName: "Total LTC Value", - field: "foreignAmount", - flex: 1, // Flex makes this column responsive - minWidth: 150, // Ensure it doesn't shrink too much - resizable: true, - }, - { - headerName: "Status", - field: "status", - flex: 1, // Flex makes this column responsive - minWidth: 300, // Ensure it doesn't shrink too much - resizable: true, - }, -]; +// const columnDefs: ColDef[] = [ +// { +// headerCheckboxSelection: false, // Adds a checkbox in the header for selecting all rows +// checkboxSelection: true, // Adds checkboxes in each row for selection +// headerName: "Select", // You can customize the header name +// width: 50, // Adjust the width as needed +// pinned: "left", // Optional, to pin this column on the left +// resizable: false, +// }, +// { +// headerName: "QORT AMOUNT", +// field: "qortAmount", +// flex: 1, // Flex makes this column responsive +// minWidth: 150, // Ensure it doesn't shrink too much +// resizable: true, +// }, +// { +// headerName: "LTC/QORT", +// valueGetter: (params) => +// +params.data.foreignAmount / +params.data.qortAmount, +// sortable: true, +// sort: "asc", +// flex: 1, // Flex makes this column responsive +// minWidth: 150, // Ensure it doesn't shrink too much +// resizable: true, +// }, +// { +// headerName: "Total LTC Value", +// field: "foreignAmount", +// flex: 1, // Flex makes this column responsive +// minWidth: 150, // Ensure it doesn't shrink too much +// resizable: true, +// }, +// { +// headerName: "Status", +// field: "status", +// flex: 1, // Flex makes this column responsive +// minWidth: 300, // Ensure it doesn't shrink too much +// resizable: true, +// }, +// ]; export default function TradeBotList({ qortAddress, failedTradeBots }) { const [tradeBotList, setTradeBotList] = useState([]); @@ -67,7 +67,7 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { const offeringTrades = useRef([]); const qortAddressRef = useRef(null); const gridRef = useRef(null); - const {updateTemporaryFailedTradeBots, fetchTemporarySellOrders, deleteTemporarySellOrder} = useContext(gameContext) + const {updateTemporaryFailedTradeBots, fetchTemporarySellOrders, deleteTemporarySellOrder, getCoinLabel, selectedCoin} = useContext(gameContext) const [open, setOpen] = useState(false) const [info, setInfo] = useState(null) const filteredOutTradeBotListWithoutFailed = useMemo(() => { @@ -88,6 +88,51 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { params.columnApi.autoSizeColumns(allColumnIds); // Automatically adjust the width to fit content }, []); + + const columnDefs: ColDef[] = useMemo(()=> { + return [ + { + headerCheckboxSelection: false, // Adds a checkbox in the header for selecting all rows + checkboxSelection: true, // Adds checkboxes in each row for selection + headerName: "Select", // You can customize the header name + width: 50, // Adjust the width as needed + pinned: "left", // Optional, to pin this column on the left + resizable: false, + }, + { + headerName: "QORT AMOUNT", + field: "qortAmount", + flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: `${getCoinLabel()}/QORT`, + valueGetter: (params) => + +params.data.foreignAmount / +params.data.qortAmount, + sortable: true, + sort: "asc", + flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: `Total ${getCoinLabel()} Value`, + field: "foreignAmount", + flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: "Status", + field: "status", + flex: 1, // Flex makes this column responsive + minWidth: 300, // Ensure it doesn't shrink too much + resizable: true, + }, + ]; + + }, [selectedCoin]) useEffect(() => { if (qortAddress) { qortAddressRef.current = qortAddress; @@ -154,38 +199,64 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { tradeBotListRef.current = sellTrades; }; + const restartTradeOffers = ()=> { + if (socketRef.current) { + socketRef.current.close(1000, 'forced'); // Close with a custom reason + socketRef.current = null + } + offeringTrades.current = [] + setTradeBotList([]); + tradeBotListRef.current = []; + } + + const socketRef = useRef(null) + const initTradeOffersWebSocket = (restarted = false) => { let tradeOffersSocketCounter = 0; let socketTimeout: any; // let socketLink = `ws://127.0.0.1:12391/websockets/crosschain/tradebot?foreignBlockchain=LITECOIN`; - let socketLink = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/websockets/crosschain/tradebot?foreignBlockchain=LITECOIN`; - const socket = new WebSocket(socketLink); - socket.onopen = () => { + let socketLink = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${baseLocalHost}/websockets/crosschain/tradebot?foreignBlockchain=${selectedCoin}`; + socketRef.current = new WebSocket(socketLink); + socketRef.current.onopen = () => { setTimeout(pingSocket, 50); tradeOffersSocketCounter += 1; }; - socket.onmessage = (e) => { + socketRef.current.onmessage = (e) => { tradeOffersSocketCounter += 1; restarted = false; processTradeBots(JSON.parse(e.data)); }; - socket.onclose = () => { + socketRef.current.onclose = (event) => { clearTimeout(socketTimeout); + if (event.reason === 'forced') { + return + } restartTradeOffersWebSocket(); }; - socket.onerror = (e) => { + socketRef.current.onerror = (e) => { clearTimeout(socketTimeout); }; const pingSocket = () => { - socket.send("ping"); + socketRef.current.send("ping"); socketTimeout = setTimeout(pingSocket, 295000); }; }; useEffect(() => { if(!qortAddress) return - initTradeOffersWebSocket(); - }, [qortAddress]); + if(selectedCoin === null) return + restartTradeOffers() + + setTimeout(() => { + initTradeOffersWebSocket() + + }, 500); + return () => { + if(socketRef.current){ + socketRef.current.close(1000, 'forced'); + } + } + }, [qortAddress, selectedCoin]); const onSelectionChanged = (event: any) => { const selectedRows = event.api.getSelectedRows(); @@ -222,7 +293,7 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { const res = await qortalRequestWithTimeout({ action: "CANCEL_TRADE_SELL_ORDER", qortAmount: selectedTrade.qortAmount, - foreignBlockchain: 'LITECOIN', + foreignBlockchain: selectedTrade.foreignBlockchain, foreignAmount: selectedTrade.foreignAmount, atAddress: selectedTrade.atAddress }, 900000); @@ -329,7 +400,7 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { }}> {/* ltcBalance ? 'red' : 'white', + color: selectedTotalLTC > foreignCoinBalance ? 'red' : 'white', }}>{selectedTotalLTC?.toFixed(4)} LTC */} @@ -340,7 +411,7 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { fontSize: '16px', color: 'white', - }}>{ltcBalance?.toFixed(4)} {foreignCoinBalance?.toFixed(4)} LTC balance */} diff --git a/src/contexts/gameContext.ts b/src/contexts/gameContext.ts index 487a536..ab2d9dd 100644 --- a/src/contexts/gameContext.ts +++ b/src/contexts/gameContext.ts @@ -14,7 +14,7 @@ export interface UserNameAvatar { } export interface IContextProps { - ltcBalance: number | null; + foreignCoinBalance: number | null; qortBalance: number | null; userInfo: any; setUserInfo: (val: any) => void; @@ -32,11 +32,14 @@ export interface IContextProps { updateTemporaryFailedTradeBots: (val: any)=> void; fetchTemporarySellOrders: ()=> void; isUsingGateway: boolean; + selectedCoin: string; + setSelectedCoin: (val: any)=> void; + getCoinLabel: ()=> void; } const defaultState: IContextProps = { qortBalance: null, - ltcBalance: null, + foreignCoinBalance: null, userInfo: null, setUserInfo: () => {}, userNameAvatar: {}, @@ -52,7 +55,10 @@ const defaultState: IContextProps = { deleteTemporarySellOrder: ()=> {}, updateTemporaryFailedTradeBots: ()=> {}, fetchTemporarySellOrders: ()=> {}, - isUsingGateway: true + isUsingGateway: true, + selectedCoin: 'LITECOIN', + setSelectedCoin: ()=> {}, + getCoinLabel: ()=> {} }; export default React.createContext(defaultState); diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index e639164..14570c5 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -24,7 +24,7 @@ export const HomePage: FC = ({}) => { const navigate = useNavigate(); const { qortBalance, - ltcBalance, + foreignCoinBalance, userInfo, isAuthenticated, setIsAuthenticated, @@ -53,7 +53,7 @@ export const HomePage: FC = ({}) => {
@@ -97,7 +97,7 @@ export const HomePage: FC = ({}) => { - +
From 0703cc387ff34da47d58bc1ccf9132fef1a1ce13 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 23 Dec 2024 09:33:51 +0200 Subject: [PATCH 03/25] fix presence --- src/components/Grids/OngoingTrades.tsx | 10 ++++--- src/components/Grids/TradeOffers.tsx | 41 ++++++++++++++++++-------- src/pages/Home/Home.tsx | 10 +++++-- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/components/Grids/OngoingTrades.tsx b/src/components/Grids/OngoingTrades.tsx index b3f93a4..65eb4d4 100644 --- a/src/components/Grids/OngoingTrades.tsx +++ b/src/components/Grids/OngoingTrades.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'; +import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import { AgGridReact } from 'ag-grid-react'; import { ColDef, RowClassParams, RowStyle, SizeColumnsToContentStrategy } from 'ag-grid-community'; import 'ag-grid-community/styles/ag-grid.css'; @@ -13,7 +13,9 @@ export const OngoingTrades = () => { const { onGoingTrades, getCoinLabel, selectedCoin } = useContext(gameContext); const gridRef = useRef(null) - + const filteredOngoingTrades = useMemo(()=> { + return onGoingTrades?.filter((item)=> item?.tradeInfo?.foreignBlockchain === selectedCoin) + }, [onGoingTrades, selectedCoin]) const onGridReady = useCallback((params: any) => { // params.api.sizeColumnsToFit(); // Adjust columns to fit the grid width @@ -80,14 +82,14 @@ export const OngoingTrades = () => { return ( -
+
item?.tradeInfo?.foreignBlockchain === selectedCoin)} + rowData={filteredOngoingTrades} // onRowClicked={onRowClicked} rowSelection="single" getRowId={getRowId} diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index bdd5721..8a0bd83 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -11,8 +11,8 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useModal } from '../common/useModal'; import FileSaver from 'file-saver'; -// export const baseLocalHost = window.location.host -export const baseLocalHost = '127.0.0.1:12391' +export const baseLocalHost = window.location.host +// export const baseLocalHost = '127.0.0.1:12391' interface RowData { amountQORT: number; @@ -54,8 +54,10 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { return offers.filter((item)=> !listOfOngoingTradesAts.includes(item.qortalAtAddress)) }, [listOfOngoingTradesAts, offers]) const initiatedFetchPresence = useRef(false) - - + const initiatedFetchPresenceSocket = useRef(false) + + const socketRef = useRef(null) + const socketPresenceRef = useRef(null) const [selectedOffer, setSelectedOffer] = useState(null) const [selectedOffers, setSelectedOffers] = useState([]) const [record, setRecord] = useState(null) @@ -270,29 +272,33 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { } - const socket = new WebSocket(socketLink) - socket.onopen = () => { + socketPresenceRef.current = new WebSocket(socketLink) + socketPresenceRef.current.onopen = () => { setTimeout(pingSocket, 50) } - socket.onmessage = (e) => { - tradePresenceTxns.current = JSON.parse(e.data) + socketPresenceRef.current.onmessage = (e) => { + tradePresenceTxns.current = !initiatedFetchPresenceSocket.current ? JSON.parse(e.data) : [...tradePresenceTxns.current, ...JSON.parse(e.data)] + initiatedFetchPresenceSocket.current = true processOffersWithPresence() restarted = false } - socket.onclose = () => { + socketPresenceRef.current.onclose = (event) => { clearTimeout(socketTimeout) + if (event.reason === 'forced') { + return + } restartTradePresenceWebSocket() } - socket.onerror = (e) => { + socketPresenceRef.current.onerror = (e) => { clearTimeout(socketTimeout) restartTradePresenceWebSocket() } const pingSocket = () => { - socket.send('ping') + socketPresenceRef.current.send('ping') socketTimeout = setTimeout(pingSocket, 295000) } } - const socketRef = useRef(null) + const restartTradeOffers = ()=> { if (socketRef.current) { @@ -304,6 +310,13 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { setSelectedOffer(null) } + const restartPresence = ()=> { + if (socketPresenceRef.current) { + socketPresenceRef.current.close(1000, 'forced'); // Close with a custom reason + socketPresenceRef.current = null + } + } + const initTradeOffersWebSocket = (restarted = false) => { if(socketRef.current) return @@ -352,6 +365,10 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { } getNewBlockedTrades() const intervalBlockTrades = setInterval(() => { + + initiatedFetchPresenceSocket.current = false + restartPresence() + initTradePresenceWebSocket() getNewBlockedTrades() }, 150000) diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 14570c5..e154767 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -1,4 +1,4 @@ -import { FC, useContext, useEffect, useState } from "react"; +import { FC, useContext, useEffect, useMemo, useState } from "react"; import { AppContainer } from "../../App-styles"; import axios from "axios"; @@ -30,10 +30,14 @@ export const HomePage: FC = ({}) => { setIsAuthenticated, OAuthLoading, setOAuthLoading, + onGoingTrades, + selectedCoin } = useContext(gameContext); const { setNotification } = useContext(NotificationContext); const [mode, setMode] = useState("buy"); - + const filteredOngoingTrades = useMemo(()=> { + return onGoingTrades?.filter((item)=> item?.tradeInfo?.foreignBlockchain === selectedCoin) + }, [onGoingTrades, selectedCoin]) const checkIfAuthenticated = async () => { try { setOAuthLoading(true); @@ -76,7 +80,7 @@ export const HomePage: FC = ({}) => { fontSize: "16px", }} > - My Pending Orders + {`My Pending Orders: ${filteredOngoingTrades?.length}`} From 55035f8c43cb58e998c4e6ef7a3f0a9e0aa92e52 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 23 Dec 2024 11:39:18 +0200 Subject: [PATCH 04/25] change header style --- src/App.css | 19 ++ src/App.tsx | 1 + src/components/Grids/TradeOffers.tsx | 4 +- src/components/header/Header-styles.tsx | 2 +- src/components/header/Header.tsx | 434 +++++++++++++++--------- src/pages/Home/Home.tsx | 8 +- 6 files changed, 308 insertions(+), 160 deletions(-) diff --git a/src/App.css b/src/App.css index 2c5cd42..6639473 100644 --- a/src/App.css +++ b/src/App.css @@ -124,4 +124,23 @@ src: url('./assets/fonts/Inter-SemiBold.ttf') format('truetype'); font-weight: 600; font-display: swap; +} + +::-webkit-scrollbar-track { + background-color: transparent; +} +::-webkit-scrollbar-track:hover { + background-color: transparent; +} + +::-webkit-scrollbar { + width: 14px; + height: 10px; +} + +::-webkit-scrollbar-thumb { + background-color: #444444; + border-radius: 8px; + background-clip: content-box; + border: 4px solid transparent; } \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 2fb3bd8..13fa6bb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -75,6 +75,7 @@ function App() { const [selectedCoin, setSelectedCoin] = useState("LITECOIN"); const foreignCoinBalance = useMemo(()=> { + if(balances[selectedCoin] === 0) return 0 return balances[selectedCoin] || null }, [balances, selectedCoin]) const [isAuthenticated, setIsAuthenticated] = useState(false); diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index 8a0bd83..bc00c6c 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -11,8 +11,8 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useModal } from '../common/useModal'; import FileSaver from 'file-saver'; -export const baseLocalHost = window.location.host -// export const baseLocalHost = '127.0.0.1:12391' +// export const baseLocalHost = window.location.host +export const baseLocalHost = '127.0.0.1:12391' interface RowData { amountQORT: number; diff --git a/src/components/header/Header-styles.tsx b/src/components/header/Header-styles.tsx index 7938beb..a03145c 100644 --- a/src/components/header/Header-styles.tsx +++ b/src/components/header/Header-styles.tsx @@ -110,7 +110,7 @@ export const GameSelectDropdownMenuItem = styled(Box)(({ theme }) => ({ export const Username = styled(Typography)(({ theme }) => ({ fontFamily: "Fira Sans, sans-serif", - fontSize: "16px", + fontSize: "20px", lineHeight: "19.2px", fontWeight: 400, color: theme.palette.text.primary, diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index 7f5b829..91e3e35 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -20,12 +20,16 @@ import gameContext from "../../contexts/gameContext"; import { UserContext } from "../../contexts/userContext"; import { cropAddress } from "../../utils/cropAddress"; import { BubbleCardColored1 } from "../../pages/Home/Home-Styles"; -import qtradeLogo from '../../components/common/icons/qtradeLogo.png' +import qtradeLogo from "../../components/common/icons/qtradeLogo.png"; +import qortIcon from "../../assets/img/qort.png"; import { Alert, + AppBar, Avatar, Box, Button, + Card, + CardContent, FormControlLabel, MenuItem, Select, @@ -36,12 +40,13 @@ import { } from "@mui/material"; import { sendRequestToExtension } from "../../App"; import { Terms } from "../Terms"; -import ltcIcon from '../../assets/img/ltc.png' -import btcIcon from '../../assets/img/btc.png' -import dogeIcon from '../../assets/img/doge.png' -import rvnIcon from '../../assets/img/rvn.png' -import dgbIcon from '../../assets/img/dgb.png' -import arrrIcon from '../../assets/img/arrr.png' +import ltcIcon from "../../assets/img/ltc.png"; +import btcIcon from "../../assets/img/btc.png"; +import dogeIcon from "../../assets/img/doge.png"; +import rvnIcon from "../../assets/img/rvn.png"; +import dgbIcon from "../../assets/img/dgb.png"; +import arrrIcon from "../../assets/img/arrr.png"; +import { Spacer } from "../common/Spacer"; const checkIfLocal = async () => { try { @@ -65,47 +70,67 @@ export const Label = styled("label")( ` ); -const SelectRow = ({coin})=> { - let img +const getCoinIcon = (coin)=> { + let img; + switch (coin) { - case 'LTC': - img = ltcIcon + case "LTC": + img = ltcIcon; break; - case 'BTC': - img = btcIcon + case "BTC": + img = btcIcon; break; - case 'DOGE': - img = dogeIcon + case "DOGE": + img = dogeIcon; break; - case 'RVN': - img = rvnIcon + case "RVN": + img = rvnIcon; break; - - case 'ARRR': - img = arrrIcon + + case "ARRR": + img = arrrIcon; break; - case 'DGB': - img = dgbIcon + case "DGB": + img = dgbIcon; break; default: - null + null; } - - return ( -

{coin}

- ) + return img } -export const Header = ({ qortBalance, foreignCoinBalance, mode, setMode }: any) => { +const SelectRow = ({ coin }) => { + let img = getCoinIcon(coin) + + + return ( +
+ +

{coin}

+
+ ); +}; + +export const Header = ({ + qortBalance, + foreignCoinBalance, + mode, + setMode, +}: any) => { const [openDropdown, setOpenDropdown] = useState(false); const dropdownRef = useRef(null); const buttonRef = useRef(null); @@ -122,7 +147,8 @@ export const Header = ({ qortBalance, foreignCoinBalance, mode, setMode }: any) message: "Change the node you are using at the authentication page", }); }; - const { userInfo, selectedCoin, setSelectedCoin, getCoinLabel } = useContext(gameContext); + const { userInfo, selectedCoin, setSelectedCoin, getCoinLabel } = + useContext(gameContext); const { avatar, setAvatar } = useContext(UserContext); const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ @@ -197,137 +223,235 @@ export const Header = ({ qortBalance, foreignCoinBalance, mode, setMode }: any) // }, [userInfo]); return ( - - + - - - - - - - - - - Balance: {qortBalance} QORT |{" "} - {foreignCoinBalance === null ? "N/A" : foreignCoinBalance} {getCoinLabel()} - - - {userInfo?.name ? ( - {userInfo?.name} - ) : userInfo?.address ? ( - {cropAddress(userInfo?.address)} - ) : null} - - {userInfo?.name ? ( - - {userInfo?.name?.charAt(0)?.toUpperCase()} - - ) : userInfo?.address ? ( - - ) : ( - { - window.open("https://www.qortal.dev", "_blank")?.focus(); + + - )} - - - + + } + label="Is using Gateway" + /> + + + - } - label="Is using Gateway" - /> - - - - - - - - + - {info?.message} - - - + + {userInfo?.name ? ( + + {userInfo?.name?.charAt(0)?.toUpperCase()} + + ) : userInfo?.address ? ( + + ) : null} + {userInfo?.name ? ( + {userInfo?.name} + ) : userInfo?.address ? ( + {cropAddress(userInfo?.address)} + ) : null} + + + + + + + + Total Balance + + + + {qortBalance} QORT + + + + + {foreignCoinBalance === null ? "N/A" : foreignCoinBalance}{" "} + {getCoinLabel()} + + + + + + + + + + + + + + + + + + + {info?.message} + + + + ); }; diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index e154767..bdac02b 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -54,14 +54,17 @@ export const HomePage: FC = ({}) => { }, [userInfo?.address]); return ( - -
+
+ + +
= ({}) => { + ); }; From 15d8bcbc65a98aff06201a1c047c2f20698dba6c Mon Sep 17 00:00:00 2001 From: PhilReact Date: Tue, 24 Dec 2024 01:32:14 +0200 Subject: [PATCH 05/25] fixes --- src/components/Grids/TradeOffers.tsx | 70 ++++++++++++++-------------- src/components/header/Header.tsx | 2 +- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index bc00c6c..aba8d7d 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -109,41 +109,43 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { } } - const columnDefs: ColDef[] = [ - { - headerCheckboxSelection: true, // Adds a checkbox in the header for selecting all rows - checkboxSelection: true, // Adds checkboxes in each row for selection - headerName: "Select", // You can customize the header name - width: 50, // Adjust the width as needed - pinned: 'left', // Optional, to pin this column on the left - resizable: false, - }, - { headerName: "QORT AMOUNT", field: "qortAmount" , flex: 1, // Flex makes this column responsive - minWidth: 150, // Ensure it doesn't shrink too much - resizable: true }, - { headerName: `${getCoinLabel()}/QORT`, valueGetter: (params) => +params.data.foreignAmount / +params.data.qortAmount, sortable: true, sort: 'asc', flex: 1, // Flex makes this column responsive - minWidth: 150, // Ensure it doesn't shrink too much - resizable: true }, - { headerName: `Total ${getCoinLabel()} Value`, field: "foreignAmount", flex: 1, // Flex makes this column responsive - minWidth: 150, // Ensure it doesn't shrink too much - resizable: true }, - { headerName: "Seller", field: "qortalCreator", flex: 1, // Flex makes this column responsive - minWidth: 300, // Ensure it doesn't shrink too much - resizable: true, valueGetter: (params)=> { - if(params?.data?.qortalCreator){ - if(qortalNames[params?.data?.qortalCreator]){ - return qortalNames[params?.data?.qortalCreator] - } else if(qortalNames[params?.data?.qortalCreator] === undefined){ - getName(params?.data?.qortalCreator) - - return params?.data?.qortalCreator - } else { - return params?.data?.qortalCreator - + const columnDefs: ColDef[] = useMemo(()=> { + return [ + { + headerCheckboxSelection: true, // Adds a checkbox in the header for selecting all rows + checkboxSelection: true, // Adds checkboxes in each row for selection + headerName: "", // You can customize the header name + width: 50, // Adjust the width as needed + pinned: 'left', // Optional, to pin this column on the left + resizable: false, + }, + { headerName: "QORT AMOUNT", field: "qortAmount" , flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true }, + { headerName: `${getCoinLabel()}/QORT`, valueGetter: (params) => +params.data.foreignAmount / +params.data.qortAmount, sortable: true, sort: 'asc', flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true }, + { headerName: `Total ${getCoinLabel()} Value`, field: "foreignAmount", flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true }, + { headerName: "Seller", field: "qortalCreator", flex: 1, // Flex makes this column responsive + minWidth: 300, // Ensure it doesn't shrink too much + resizable: true, valueGetter: (params)=> { + if(params?.data?.qortalCreator){ + if(qortalNames[params?.data?.qortalCreator]){ + return qortalNames[params?.data?.qortalCreator] + } else if(qortalNames[params?.data?.qortalCreator] === undefined){ + getName(params?.data?.qortalCreator) + + return params?.data?.qortalCreator + } else { + return params?.data?.qortalCreator + + } } - } - } }, - ]; + } }, + ]; + },[qortalNames]) diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index 91e3e35..c94dc14 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -345,7 +345,7 @@ export const Header = ({ }} > Date: Tue, 24 Dec 2024 06:23:14 +0200 Subject: [PATCH 06/25] fix socket bug memory leak --- src/components/Grids/TradeOffers.tsx | 34 ++++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index aba8d7d..e7f25ca 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -109,6 +109,23 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { } } + const restartTradeOffers = ()=> { + if (socketRef.current) { + socketRef.current.close(1000, 'forced'); // Close with a custom reason + socketRef.current = null + } + offeringTrades.current = [] + setOffers([]) + setSelectedOffer(null) + } + + const restartPresence = ()=> { + if (socketPresenceRef.current) { + socketPresenceRef.current.close(1000, 'forced'); // Close with a custom reason + socketPresenceRef.current = null + } + } + const columnDefs: ColDef[] = useMemo(()=> { return [ { @@ -260,10 +277,12 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { } const restartTradeOffersWebSocket = () => { + restartPresence() setTimeout(() => initTradeOffersWebSocket(true), 50) } const initTradePresenceWebSocket = (restarted = false) => { + if(socketPresenceRef.current) return let socketTimeout: any let socketLink if(isUsingGateway){ @@ -302,22 +321,7 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { } - const restartTradeOffers = ()=> { - if (socketRef.current) { - socketRef.current.close(1000, 'forced'); // Close with a custom reason - socketRef.current = null - } - offeringTrades.current = [] - setOffers([]) - setSelectedOffer(null) - } - const restartPresence = ()=> { - if (socketPresenceRef.current) { - socketPresenceRef.current.close(1000, 'forced'); // Close with a custom reason - socketPresenceRef.current = null - } - } const initTradeOffersWebSocket = (restarted = false) => { From bf186b797b174c2352a94b68f02b4d0e25065a63 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Tue, 24 Dec 2024 07:01:23 +0200 Subject: [PATCH 07/25] fix --- src/components/Grids/TradeOffers.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index e7f25ca..2807901 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -11,8 +11,8 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useModal } from '../common/useModal'; import FileSaver from 'file-saver'; -// export const baseLocalHost = window.location.host -export const baseLocalHost = '127.0.0.1:12391' +export const baseLocalHost = window.location.host +// export const baseLocalHost = '127.0.0.1:12391' interface RowData { amountQORT: number; @@ -173,6 +173,7 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { // }; const restartTradePresenceWebSocket = () => { + restartPresence() setTimeout(() => initTradePresenceWebSocket(true), 50) } @@ -277,7 +278,6 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { } const restartTradeOffersWebSocket = () => { - restartPresence() setTimeout(() => initTradeOffersWebSocket(true), 50) } From 842b109b0d50ef77cc257d1c670ebc9ccbacee6a Mon Sep 17 00:00:00 2001 From: Justin Ferrari <‘justinwesleyferrari@gmail.com’> Date: Tue, 24 Dec 2024 16:21:16 -0500 Subject: [PATCH 08/25] Initial UI changes --- src/App-styles.tsx | 5 +- src/components/Grids/Table-styles.tsx | 30 +++- src/components/Grids/TradeOffers.tsx | 16 +- src/components/header/Header-styles.tsx | 69 +++++++- src/components/header/Header.tsx | 209 +++++++++--------------- src/pages/Home/Home-Styles.tsx | 113 ++++++------- src/pages/Home/Home.tsx | 123 +++++++------- 7 files changed, 288 insertions(+), 277 deletions(-) diff --git a/src/App-styles.tsx b/src/App-styles.tsx index b50acf2..68b87ab 100644 --- a/src/App-styles.tsx +++ b/src/App-styles.tsx @@ -2,13 +2,12 @@ import { Box } from "@mui/material"; import { styled } from "@mui/system"; export const AppContainer = styled(Box)(({ theme }) => ({ - width: "100%", height: "100%", display: "flex", flexDirection: "column", alignItems: "flex-start", - padding: "1em 0", - paddingBottom: '50px' + padding: "20px 30px 0 30px", + backgroundColor: "#323336" })); export const MainContainer = styled(Box)` diff --git a/src/components/Grids/Table-styles.tsx b/src/components/Grids/Table-styles.tsx index f6feb41..8597b9f 100644 --- a/src/components/Grids/Table-styles.tsx +++ b/src/components/Grids/Table-styles.tsx @@ -1,11 +1,23 @@ -import { styled } from "@mui/system"; -import { Box, Typography } from "@mui/material"; +import { Box, styled } from "@mui/system"; +import { Typography } from "@mui/material"; export const TextTableTitle = styled(Typography)(({ theme }) => ({ - fontFamily: "Inter", - color: theme.palette.text.primary, - fontWeight: 400, - fontSize: "20px", - lineHeight: "40px", - userSelect: "none", - })); \ No newline at end of file + fontFamily: "Inter", + color: theme.palette.text.primary, + fontWeight: 400, + fontSize: "20px", + lineHeight: "40px", + userSelect: "none", +})); + +export const BuyContainer = styled(Box)({ + width: "calc(100% - 60px)", + display: "flex", + justifyContent: "space-between", + alignItems: "center", + position: "fixed", + bottom: "0px", + height: "100px", + padding: "7px 14px", + background: "#323336", +}); diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index 2807901..9b9f599 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -10,6 +10,7 @@ import gameContext from '../../contexts/gameContext'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useModal } from '../common/useModal'; import FileSaver from 'file-saver'; +import { BuyContainer } from './Table-styles'; export const baseLocalHost = window.location.host // export const baseLocalHost = '127.0.0.1:12391' @@ -587,18 +588,7 @@ const handleClose = (
- + {`${getCoinLabel()} `} balance {BuyButton()} - + ({ }, })); +export const BubbleCardColored1 = styled(Box)({ + height: "77px", + width: "77px", + background: "linear-gradient(124.49deg, #70BAFF 7.03%, #F29999 94.22%)", + boxShadow: "0px 0px 25.8px -1px #1C5A93", + borderRadius: "50%", +}); + export const HomeIcon = styled(HomeSVG)({ cursor: "pointer", }); @@ -135,7 +143,7 @@ export const RightColumn = styled(Box)({ flexDirection: "row", gap: "10px", alignItems: "flex-start", - padding: '10px' + padding: "10px", }); export const AvatarCircle = styled("img")({ borderRadius: "50%", @@ -145,12 +153,67 @@ export const AvatarCircle = styled("img")({ userSelect: "none", }); - export const HeaderText = styled(Typography)(({ theme }) => ({ fontFamily: "Inter", color: theme.palette.text.primary, + textAlign: "center", fontWeight: 500, fontSize: "16px", lineHeight: 1.2, userSelect: "none", })); + +export const TotalCol = styled(Box)({ + display: "flex", + flexDirection: "column", + gap: "5px", +}); + +export const CoinActionsRow = styled(Box)({ + display: "flex", + flexDirection: "row", + gap: "5px", + alignItems: "center", + justifyContent: "center", +}); + +export const CoinSendBtn = styled(Button)(({ theme }) => ({ + backgroundColor: theme.palette.primary.main, + color: "#000000", + border: `1px solid ${theme.palette.primary.main}`, + fontFamily: "Inter, sans-serif", + fontWeight: 500, + fontSize: "14px", + lineHeight: "16px", + padding: "5px 10px", + borderRadius: "0px", + transition: "all 0.3s ease-in-out", + "&:hover": { + border: `1px solid ${theme.palette.text.primary}`, + backgroundColor: theme.palette.text.primary, + }, +})); + +export const CoinReceiveBtn = styled(Button)(({ theme }) => ({ + backgroundColor: "transparent", + color: theme.palette.text.primary, + border: `1px solid ${theme.palette.text.primary}`, + fontFamily: "Inter, sans-serif", + fontWeight: 500, + fontSize: "14px", + lineHeight: "16px", + padding: "5px 10px", + borderRadius: "0px", + transition: "all 0.3s ease-in-out", + "&:hover": { + color: "#000000", + backgroundColor: theme.palette.text.primary, + }, +})); + +export const CoinSelectRow = styled(Box)({ + display: "flex", + flexDirection: "column", + gap: "5px", + alignSelf: "flex-start", +}); diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index c94dc14..f4e5515 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -1,25 +1,21 @@ import { useState, useEffect, useRef, useContext, ChangeEvent } from "react"; -import ReactGA from "react-ga4"; import { - AvatarCircle, - CaretDownIcon, - DropdownContainer, - GameSelectDropdown, - GameSelectDropdownMenu, - GameSelectDropdownMenuItem, + BubbleCardColored1, + CoinActionsRow, + CoinReceiveBtn, + CoinSelectRow, + CoinSendBtn, HeaderNav, HeaderText, - HomeIcon, LogoColumn, NameRow, - QortalLogoIcon, RightColumn, + TotalCol, Username, } from "./Header-styles"; import gameContext from "../../contexts/gameContext"; import { UserContext } from "../../contexts/userContext"; import { cropAddress } from "../../utils/cropAddress"; -import { BubbleCardColored1 } from "../../pages/Home/Home-Styles"; import qtradeLogo from "../../components/common/icons/qtradeLogo.png"; import qortIcon from "../../assets/img/qort.png"; import { @@ -27,7 +23,6 @@ import { AppBar, Avatar, Box, - Button, Card, CardContent, FormControlLabel, @@ -70,7 +65,7 @@ export const Label = styled("label")( ` ); -const getCoinIcon = (coin)=> { +const getCoinIcon = (coin) => { let img; switch (coin) { @@ -97,12 +92,11 @@ const getCoinIcon = (coin)=> { default: null; } - return img -} + return img; +}; const SelectRow = ({ coin }) => { - let img = getCoinIcon(coin) - + let img = getCoinIcon(coin); return (
{ ); }; -export const Header = ({ - qortBalance, - foreignCoinBalance, - mode, - setMode, -}: any) => { +export const Header = ({ qortBalance, foreignCoinBalance }: any) => { const [openDropdown, setOpenDropdown] = useState(false); const dropdownRef = useRef(null); const buttonRef = useRef(null); @@ -320,122 +309,82 @@ export const Header = ({ Total Balance - - + - {qortBalance} QORT - + > + + {qortBalance} QORT + + + Send + Receive + + - - + - {foreignCoinBalance === null ? "N/A" : foreignCoinBalance}{" "} - {getCoinLabel()} - - + > + + {foreignCoinBalance === null ? "N/A" : foreignCoinBalance}{" "} + {getCoinLabel()} + + + Send + Receive + + - - + setSelectedCoin(e.target.value)} - > - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + ({ - display: "flex", - alignItems: "center", - justifyContent: "center", - height: "77px", - width: "77px", - background: "#ffffff05", - borderRadius: "50%", - fontFamily: "Fredoka One, sans-serif", - fontWeight: 500, - fontSize: "40px", - lineHeight: "48.4px", - textAlign: "center", - color: theme.palette.text.primary, -})); - - -export const BubbleCardColored1 = styled(Box)({ - height: "77px", - width: "77px", - background: "linear-gradient(124.49deg, #70BAFF 7.03%, #F29999 94.22%)", - boxShadow: "0px 0px 25.8px -1px #1C5A93", - borderRadius: "50%", -}); - -export const BubbleCardColored2 = styled(Box)({ - height: "77px", - width: "77px", - background: "linear-gradient(36.5deg, #70BAFF 19.69%, #F29999 90.73%)", - boxShadow: "0px 0px 25.8px -1px #1C5A93", - borderRadius: "50%", -}); - -export const BubbleCardColored3 = styled(Box)({ - height: "77px", - width: "77px", - background: "linear-gradient(180deg, #70BAFF -24.68%, #ACABD0 25.49%, #F29999 74.03%)", - boxShadow: "0px 0px 25.8px -1px #1C5A93", - borderRadius: "50%", -}); - -export const BubbleCardColored4 = styled(Box)({ - height: "77px", - width: "77px", - background: "linear-gradient(275.71deg, #70BAFF 35.99%, #F29999 95.61%)", - boxShadow: "0px 0px 25.8px -1px #1C5A93", - borderRadius: "50%", -}); +type TabProp = { + activeTab: boolean; +}; export const MainCol = styled(Box)({ display: "flex", @@ -119,4 +64,54 @@ export const HomeWrapper = styled(Box)({ gap: "100px", height: "90vh", width: "100%", -}); \ No newline at end of file +}); + +export const TabsContainer = styled(Box)({ + display: "flex", + flexDirection: "column", + gap: "15px", + alignItems: "flex-start", + width: "100%", + justifyContent: "center", +}); + +export const TabsRow = styled(Box)({ + display: "flex", + flexDirection: "row", + justifyContent: "space-evenly", + alignItems: "center", + backgroundColor: "#323336", + width: "fit-content", + borderRadius: "5px", + padding: "3px 0", +}); + +export const Tab = styled(Box, { + shouldForwardProp: (prop) => prop !== "activeTab" +})(({ theme, activeTab }) => ({ + color: activeTab ? "#323336" : "#e8e8e8", + fontFamily: "Inter, sans-serif", + fontSize: "16px", + lineHeight: "19.2px", + fontWeight: 400, + backgroundColor: activeTab ? "#e8e8e8" : "transparent", + padding: "5px 10px", + borderRadius: "5px", + height: "auto", + transition: "all 0.4s ease-in-out", + userSelect: "none", + "&:hover": { + color: "#323336", + backgroundColor: "#babbbc", + cursor: activeTab ? "auto" : "pointer", + }, +})); + +export const TabDivider = styled(Box, { + shouldForwardProp: (prop) => prop !== "activeTab" +})(({ theme, activeTab }) => ({ + width: "1px", + height: "25px", + margin: "0 3px", + backgroundColor: activeTab ? "transparent" : "#a4a4a5", +})); \ No newline at end of file diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index bdac02b..f732c34 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -1,49 +1,40 @@ -import { FC, useContext, useEffect, useMemo, useState } from "react"; +import { useContext, useEffect, useMemo, useState } from "react"; import { AppContainer } from "../../App-styles"; - -import axios from "axios"; import { Header } from "../../components/header/Header"; -import { useLocation, useNavigate } from "react-router-dom"; import gameContext from "../../contexts/gameContext"; -import { Link } from "react-router-dom"; import { NotificationContext } from "../../contexts/notificationContext"; - import { TradeOffers } from "../../components/Grids/TradeOffers"; import { OngoingTrades } from "../../components/Grids/OngoingTrades"; -import { Box, Button, CircularProgress } from "@mui/material"; +import { Box } from "@mui/material"; import { TextTableTitle } from "../../components/Grids/Table-styles"; import { Spacer } from "../../components/common/Spacer"; import { ReusableModal } from "../../components/common/reusable-modal/ReusableModal"; -import { OAuthButton, OAuthButtonRow } from "./Home-Styles"; +import { Tab, TabDivider, TabsContainer, TabsRow } from "./Home-Styles"; import { CreateSell } from "../../components/sell/CreateSell"; -interface IsInstalledProps {} - -export const HomePage: FC = ({}) => { - const location = useLocation(); - const navigate = useNavigate(); +export const HomePage = () => { const { qortBalance, foreignCoinBalance, userInfo, - isAuthenticated, setIsAuthenticated, - OAuthLoading, setOAuthLoading, onGoingTrades, - selectedCoin + selectedCoin, } = useContext(gameContext); - const { setNotification } = useContext(NotificationContext); const [mode, setMode] = useState("buy"); - const filteredOngoingTrades = useMemo(()=> { - return onGoingTrades?.filter((item)=> item?.tradeInfo?.foreignBlockchain === selectedCoin) - }, [onGoingTrades, selectedCoin]) + const filteredOngoingTrades = useMemo(() => { + return onGoingTrades?.filter( + (item) => item?.tradeInfo?.foreignBlockchain === selectedCoin + ); + }, [onGoingTrades, selectedCoin]); const checkIfAuthenticated = async () => { try { setOAuthLoading(true); setIsAuthenticated(true); } catch (error) { + console.error(error); } finally { setOAuthLoading(false); } @@ -55,60 +46,72 @@ export const HomePage: FC = ({}) => { return ( <> -
- - - -
- - + + + setMode("buy")}> + Buy QORT + + + setMode("sell")}> + Sell QORT + + + setMode("history")} + > + Trade History + + + +
- + - {`My Pending Orders: ${filteredOngoingTrades?.length}`} - - - - - - - + {`My Pending Orders: ${filteredOngoingTrades?.length}`} + + + + + + - Open Market Sell Orders - - - - -
+ + Open Market Sell Orders + +
+ + +
- -
+ + ); }; From 8a8239f5bbbef8f922d8d32780cb07b2e62535a8 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 25 Dec 2024 07:12:33 +0200 Subject: [PATCH 09/25] added buying modal --- package-lock.json | 106 ++- package.json | 2 + src/components/Grids/TradeOffers.tsx | 1139 ++++++++++++++++---------- src/components/sell/CreateSell.tsx | 4 +- 4 files changed, 807 insertions(+), 444 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9a44d49..d6a4b93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,8 +19,10 @@ "lodash": "^4.17.21", "moment": "^2.30.1", "react": "^18.2.0", + "react-countdown-circle-timer": "^3.2.1", "react-dom": "^18.2.0", "react-ga4": "^2.1.0", + "react-loader-spinner": "^6.1.6", "react-router-dom": "^6.23.0", "react-toastify": "^10.0.5", "sass": "^1.76.0", @@ -3236,6 +3238,11 @@ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" + }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -3826,6 +3833,14 @@ "node": ">=6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001660", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", @@ -4003,6 +4018,24 @@ "node": ">=8" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -5984,7 +6017,6 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -6218,7 +6250,6 @@ "version": "8.4.38", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -6242,6 +6273,11 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -6332,6 +6368,14 @@ "node": ">=0.10.0" } }, + "node_modules/react-countdown-circle-timer": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/react-countdown-circle-timer/-/react-countdown-circle-timer-3.2.1.tgz", + "integrity": "sha512-yBAy/9ILXOiFbLBM+3jS72TW5LeRcH8wkRC9NNqMpUkCXkGjSnaeRbJMsR9lsYF0oVXjSDbJaRbCuVMT+9HnKA==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -6354,6 +6398,22 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "node_modules/react-loader-spinner": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-6.1.6.tgz", + "integrity": "sha512-x5h1Jcit7Qn03MuKlrWcMG9o12cp9SNDVHVJTNRi9TgtGPKcjKiXkou4NRfLAtXaFB3+Z8yZsVzONmPzhv2ErA==", + "dependencies": { + "react-is": "^18.2.0", + "styled-components": "^6.1.2" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -6788,6 +6848,11 @@ "node": ">= 0.4" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7041,6 +7106,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/styled-components": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.13.tgz", + "integrity": "sha512-M0+N2xSnAtwcVAQeFEsGWFFxXDftHUD7XrKla06QbpUMmbmtFBMMTcKWvFXtWxuD5qQkB8iU5gk6QASlx2ZRMw==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.38", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -7210,6 +7307,11 @@ "typescript": ">=4.2.0" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 25ab599..f23f0d3 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,10 @@ "lodash": "^4.17.21", "moment": "^2.30.1", "react": "^18.2.0", + "react-countdown-circle-timer": "^3.2.1", "react-dom": "^18.2.0", "react-ga4": "^2.1.0", + "react-loader-spinner": "^6.1.6", "react-router-dom": "^6.23.0", "react-toastify": "^10.0.5", "sass": "^1.76.0", diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index 2807901..cabf34c 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -1,18 +1,47 @@ -import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; -import { AgGridReact } from 'ag-grid-react'; -import { ColDef, RowClassParams, RowStyle, SizeColumnsToContentStrategy } from 'ag-grid-community'; -import 'ag-grid-community/styles/ag-grid.css'; -import 'ag-grid-community/styles/ag-theme-alpine.css'; -import axios from 'axios'; -import { sendRequestToExtension } from '../../App'; -import { Alert, Box, Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Snackbar, SnackbarCloseReason, Typography } from '@mui/material'; -import gameContext from '../../contexts/gameContext'; -import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; -import { useModal } from '../common/useModal'; -import FileSaver from 'file-saver'; +import React, { + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { AgGridReact } from "ag-grid-react"; +import { + ColDef, + RowClassParams, + RowStyle, + SizeColumnsToContentStrategy, +} from "ag-grid-community"; +import "ag-grid-community/styles/ag-grid.css"; +import "ag-grid-community/styles/ag-theme-alpine.css"; +import axios from "axios"; +import { sendRequestToExtension } from "../../App"; +import { + Alert, + Box, + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Snackbar, + SnackbarCloseReason, + Typography, +} from "@mui/material"; +import gameContext from "../../contexts/gameContext"; +import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; +import { useModal } from "../common/useModal"; +import FileSaver from "file-saver"; +import { Spacer } from "../common/Spacer"; +import { Hourglass } from "react-loader-spinner"; +import ErrorIcon from "@mui/icons-material/Error"; +import CheckCircleIcon from "@mui/icons-material/CheckCircle"; +import { CountdownCircleTimer } from "react-countdown-circle-timer"; -export const baseLocalHost = window.location.host -// export const baseLocalHost = '127.0.0.1:12391' +// export const baseLocalHost = window.location.host +export const baseLocalHost = "127.0.0.1:12391"; interface RowData { amountQORT: number; @@ -22,26 +51,35 @@ interface RowData { } export const saveFileToDisk = async (data) => { - const dataString = JSON.stringify(data); - const blob = new Blob([dataString], { type: 'application/json' }); -const fileName = "traderecord_" + Date.now() + '_' + ".json"; + const blob = new Blob([dataString], { type: "application/json" }); + const fileName = "traderecord_" + Date.now() + "_" + ".json"; -await FileSaver.saveAs(blob, fileName); - -} - -export const autoSizeStrategy: SizeColumnsToContentStrategy = { - type: 'fitCellContents' + await FileSaver.saveAs(blob, fileName); }; -export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { - const [offers, setOffers] = useState([]) - const [qortalNames, setQortalNames] = useState({}) - const { fetchOngoingTransactions, onGoingTrades, updateTransactionInDB, isUsingGateway, getCoinLabel, selectedCoin } = useContext(gameContext); - const listOfOngoingTradesAts = useMemo(()=> { - return onGoingTrades?.filter((item)=> item?.status !== 'trade-failed')?.map((trade)=> trade?.qortalAtAddress) || [] - }, [onGoingTrades]) +export const autoSizeStrategy: SizeColumnsToContentStrategy = { + type: "fitCellContents", +}; + +export const TradeOffers: React.FC = ({ foreignCoinBalance }: any) => { + const [offers, setOffers] = useState([]); + const [qortalNames, setQortalNames] = useState({}); + const { + fetchOngoingTransactions, + onGoingTrades, + updateTransactionInDB, + isUsingGateway, + getCoinLabel, + selectedCoin, + } = useContext(gameContext); + const listOfOngoingTradesAts = useMemo(() => { + return ( + onGoingTrades + ?.filter((item) => item?.status !== "trade-failed") + ?.map((trade) => trade?.qortalAtAddress) || [] + ); + }, [onGoingTrades]); const { isShow: isShowInfo, onCancel: onCancelInfo, @@ -50,121 +88,149 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { message: messageInfo, } = useModal(); - const offersWithoutOngoing = useMemo(()=> { - return offers.filter((item)=> !listOfOngoingTradesAts.includes(item.qortalAtAddress)) - }, [listOfOngoingTradesAts, offers]) - const initiatedFetchPresence = useRef(false) - const initiatedFetchPresenceSocket = useRef(false) + const offersWithoutOngoing = useMemo(() => { + return offers.filter( + (item) => !listOfOngoingTradesAts.includes(item.qortalAtAddress) + ); + }, [listOfOngoingTradesAts, offers]); + const initiatedFetchPresence = useRef(false); + const initiatedFetchPresenceSocket = useRef(false); + const [isShowBuyInProgress, setIsShowBuyInProgress] = useState(null); + const socketRef = useRef(null); + const socketPresenceRef = useRef(null); + const [selectedOffer, setSelectedOffer] = useState(null); + const [selectedOffers, setSelectedOffers] = useState([]); + const [record, setRecord] = useState(null); + const tradePresenceTxns = useRef([]); + const offeringTrades = useRef([]); + const blockedTradesList = useRef([]); + const gridRef = useRef(null); - const socketRef = useRef(null) - const socketPresenceRef = useRef(null) - const [selectedOffer, setSelectedOffer] = useState(null) - const [selectedOffers, setSelectedOffers] = useState([]) - const [record, setRecord] = useState(null) - const tradePresenceTxns = useRef([]) - const offeringTrades = useRef([]) - const blockedTradesList = useRef([]) - const gridRef = useRef(null) - - - const [open, setOpen] = useState(false) - const [info, setInfo] = useState(null) + const [open, setOpen] = useState(false); + const [info, setInfo] = useState(null); const BuyButton = () => { return ( - ); }; - + const defaultColDef = { resizable: true, // Make columns resizable by default sortable: true, // Make columns sortable by default suppressMovable: true, // Prevent columns from being movable }; - const getName = async (address)=> { + const getName = async (address) => { try { const response = await fetch("/names/address/" + address); - const nameData = await response.json(); - if (nameData?.length > 0) { - setQortalNames((prev)=> { - return { - ...prev, - [address]: nameData[0].name - } - }) - } else { - setQortalNames((prev)=> { - return { - ...prev, - [address]: null - } - }) - } + const nameData = await response.json(); + if (nameData?.length > 0) { + setQortalNames((prev) => { + return { + ...prev, + [address]: nameData[0].name, + }; + }); + } else { + setQortalNames((prev) => { + return { + ...prev, + [address]: null, + }; + }); + } } catch (error) { // error } - } + }; - const restartTradeOffers = ()=> { + const restartTradeOffers = () => { if (socketRef.current) { - socketRef.current.close(1000, 'forced'); // Close with a custom reason - socketRef.current = null + socketRef.current.close(1000, "forced"); // Close with a custom reason + socketRef.current = null; } - offeringTrades.current = [] - setOffers([]) - setSelectedOffer(null) - } + offeringTrades.current = []; + setOffers([]); + setSelectedOffer(null); + }; - const restartPresence = ()=> { + const restartPresence = () => { if (socketPresenceRef.current) { - socketPresenceRef.current.close(1000, 'forced'); // Close with a custom reason - socketPresenceRef.current = null + socketPresenceRef.current.close(1000, "forced"); // Close with a custom reason + socketPresenceRef.current = null; } - } + }; - const columnDefs: ColDef[] = useMemo(()=> { + const columnDefs: ColDef[] = useMemo(() => { return [ - { + { headerCheckboxSelection: true, // Adds a checkbox in the header for selecting all rows checkboxSelection: true, // Adds checkboxes in each row for selection headerName: "", // You can customize the header name width: 50, // Adjust the width as needed - pinned: 'left', // Optional, to pin this column on the left + pinned: "left", // Optional, to pin this column on the left resizable: false, }, - { headerName: "QORT AMOUNT", field: "qortAmount" , flex: 1, // Flex makes this column responsive - minWidth: 150, // Ensure it doesn't shrink too much - resizable: true }, - { headerName: `${getCoinLabel()}/QORT`, valueGetter: (params) => +params.data.foreignAmount / +params.data.qortAmount, sortable: true, sort: 'asc', flex: 1, // Flex makes this column responsive - minWidth: 150, // Ensure it doesn't shrink too much - resizable: true }, - { headerName: `Total ${getCoinLabel()} Value`, field: "foreignAmount", flex: 1, // Flex makes this column responsive - minWidth: 150, // Ensure it doesn't shrink too much - resizable: true }, - { headerName: "Seller", field: "qortalCreator", flex: 1, // Flex makes this column responsive - minWidth: 300, // Ensure it doesn't shrink too much - resizable: true, valueGetter: (params)=> { - if(params?.data?.qortalCreator){ - if(qortalNames[params?.data?.qortalCreator]){ - return qortalNames[params?.data?.qortalCreator] - } else if(qortalNames[params?.data?.qortalCreator] === undefined){ - getName(params?.data?.qortalCreator) - - return params?.data?.qortalCreator - } else { - return params?.data?.qortalCreator - - } - } - } }, - ]; - },[qortalNames]) + { + headerName: "QORT AMOUNT", + field: "qortAmount", + flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: `${getCoinLabel()}/QORT`, + valueGetter: (params) => + +params.data.foreignAmount / +params.data.qortAmount, + sortable: true, + sort: "asc", + flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: `Total ${getCoinLabel()} Value`, + field: "foreignAmount", + flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: "Seller", + field: "qortalCreator", + flex: 1, // Flex makes this column responsive + minWidth: 300, // Ensure it doesn't shrink too much + resizable: true, + valueGetter: (params) => { + if (params?.data?.qortalCreator) { + if (qortalNames[params?.data?.qortalCreator]) { + return qortalNames[params?.data?.qortalCreator]; + } else if (qortalNames[params?.data?.qortalCreator] === undefined) { + getName(params?.data?.qortalCreator); - + return params?.data?.qortalCreator; + } else { + return params?.data?.qortalCreator; + } + } + }, + }, + ]; + }, [qortalNames]); // const onRowClicked = (event: any) => { // if(listOfOngoingTradesAts.includes(event.data.qortalAtAddress)) return @@ -173,235 +239,256 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { // }; const restartTradePresenceWebSocket = () => { - restartPresence() - setTimeout(() => initTradePresenceWebSocket(true), 50) - } - - + restartPresence(); + setTimeout(() => initTradePresenceWebSocket(true), 50); + }; const getNewBlockedTrades = async () => { const unconfirmedTransactionsList = async () => { + const unconfirmedTransactionslUrl = `/transactions/unconfirmed?txType=MESSAGE&limit=0&reverse=true`; - const unconfirmedTransactionslUrl = `/transactions/unconfirmed?txType=MESSAGE&limit=0&reverse=true` + var addBlockedTrades = JSON.parse( + localStorage.getItem("failedTrades") || "[]" + ); - var addBlockedTrades = JSON.parse(localStorage.getItem('failedTrades') || '[]') - - await fetch(unconfirmedTransactionslUrl).then(response => { - return response.json() - }).then(data => { - data.map((item: any) => { - const unconfirmedNessageTimeDiff = Date.now() - item.timestamp - const timeOneHour = 60 * 60 * 1000 - if (Number(unconfirmedNessageTimeDiff) > Number(timeOneHour)) { - const addBlocked = { - timestamp: item.timestamp, - recipient: item.recipient - } - addBlockedTrades.push(addBlocked) - } + await fetch(unconfirmedTransactionslUrl) + .then((response) => { + return response.json(); }) - localStorage.setItem("failedTrades", JSON.stringify(addBlockedTrades)) - blockedTradesList.current = JSON.parse(localStorage.getItem('failedTrades') || '[]') - }) - } + .then((data) => { + data.map((item: any) => { + const unconfirmedNessageTimeDiff = Date.now() - item.timestamp; + const timeOneHour = 60 * 60 * 1000; + if (Number(unconfirmedNessageTimeDiff) > Number(timeOneHour)) { + const addBlocked = { + timestamp: item.timestamp, + recipient: item.recipient, + }; + addBlockedTrades.push(addBlocked); + } + }); + localStorage.setItem( + "failedTrades", + JSON.stringify(addBlockedTrades) + ); + blockedTradesList.current = JSON.parse( + localStorage.getItem("failedTrades") || "[]" + ); + }); + }; - await unconfirmedTransactionsList() + await unconfirmedTransactionsList(); const filterUnconfirmedTransactionsList = async () => { - let cleanBlockedTrades = blockedTradesList.current.reduce((newArray, cut: any) => { - if (cut && !newArray.some((obj: any) => obj.recipient === cut.recipient)) { - newArray.push(cut) - } - return newArray - }, [] as any[]) - localStorage.setItem("failedTrades", JSON.stringify(cleanBlockedTrades)) - blockedTradesList.current = JSON.parse(localStorage.getItem("failedTrades") || "[]") - } + let cleanBlockedTrades = blockedTradesList.current.reduce( + (newArray, cut: any) => { + if ( + cut && + !newArray.some((obj: any) => obj.recipient === cut.recipient) + ) { + newArray.push(cut); + } + return newArray; + }, + [] as any[] + ); + localStorage.setItem("failedTrades", JSON.stringify(cleanBlockedTrades)); + blockedTradesList.current = JSON.parse( + localStorage.getItem("failedTrades") || "[]" + ); + }; - await filterUnconfirmedTransactionsList() - processOffersWithPresence() - } + await filterUnconfirmedTransactionsList(); + processOffersWithPresence(); + }; - const executeGetNewBlockTrades = useCallback(()=> { - getNewBlockedTrades() - - }, []) + const executeGetNewBlockTrades = useCallback(() => { + getNewBlockedTrades(); + }, []); useEffect(() => { subscribeToEvent("execute-get-new-block-trades", executeGetNewBlockTrades); return () => { - unsubscribeFromEvent("execute-get-new-block-trades", executeGetNewBlockTrades); + unsubscribeFromEvent( + "execute-get-new-block-trades", + executeGetNewBlockTrades + ); }; }, []); const processOffersWithPresence = () => { - if (offeringTrades.current === null) return + if (offeringTrades.current === null) return; async function asyncForEach(array: any, callback: any) { for (let index = 0; index < array.length; index++) { - await callback(array[index], index, array) + await callback(array[index], index, array); } } - const filterOffersUsingTradePresence = (offeringTrade: any) => { return offeringTrade.tradePresenceExpiry > Date.now(); - } + }; const startOfferPresenceMapping = async () => { if (tradePresenceTxns.current) { for (const tradePresence of tradePresenceTxns.current) { - const offerIndex = offeringTrades.current.findIndex(offeringTrade => offeringTrade.qortalCreatorTradeAddress === tradePresence.tradeAddress); + const offerIndex = offeringTrades.current.findIndex( + (offeringTrade) => + offeringTrade.qortalCreatorTradeAddress === + tradePresence.tradeAddress + ); if (offerIndex !== -1) { - offeringTrades.current[offerIndex].tradePresenceExpiry = tradePresence.timestamp; + offeringTrades.current[offerIndex].tradePresenceExpiry = + tradePresence.timestamp; } } } - let filteredOffers = offeringTrades.current?.filter((offeringTrade) => filterOffersUsingTradePresence(offeringTrade)) || [] - let tradesPresenceCleaned: any[] = filteredOffers - + let filteredOffers = + offeringTrades.current?.filter((offeringTrade) => + filterOffersUsingTradePresence(offeringTrade) + ) || []; + let tradesPresenceCleaned: any[] = filteredOffers; blockedTradesList.current.forEach((item: any) => { - const toDelete = item.recipient - tradesPresenceCleaned = tradesPresenceCleaned?.filter(el => { - return el.qortalCreatorTradeAddress !== toDelete - }) || [] - }) + const toDelete = item.recipient; + tradesPresenceCleaned = + tradesPresenceCleaned?.filter((el) => { + return el.qortalCreatorTradeAddress !== toDelete; + }) || []; + }); if (tradesPresenceCleaned) { - updateGridData(tradesPresenceCleaned) + updateGridData(tradesPresenceCleaned); } - } + }; - startOfferPresenceMapping() - } + startOfferPresenceMapping(); + }; const restartTradeOffersWebSocket = () => { - setTimeout(() => initTradeOffersWebSocket(true), 50) - } + setTimeout(() => initTradeOffersWebSocket(true), 50); + }; const initTradePresenceWebSocket = (restarted = false) => { - if(socketPresenceRef.current) return - let socketTimeout: any - let socketLink - if(isUsingGateway){ - socketLink = `wss://appnode.qortal.org/websockets/crosschain/tradepresence` + if (socketPresenceRef.current) return; + let socketTimeout: any; + let socketLink; + if (isUsingGateway) { + socketLink = `wss://appnode.qortal.org/websockets/crosschain/tradepresence`; } else { - socketLink = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${baseLocalHost}/websockets/crosschain/tradepresence`; - + socketLink = `${ + window.location.protocol === "https:" ? "wss:" : "ws:" + }//${baseLocalHost}/websockets/crosschain/tradepresence`; } - - - socketPresenceRef.current = new WebSocket(socketLink) + + socketPresenceRef.current = new WebSocket(socketLink); socketPresenceRef.current.onopen = () => { - setTimeout(pingSocket, 50) - } + setTimeout(pingSocket, 50); + }; socketPresenceRef.current.onmessage = (e) => { - tradePresenceTxns.current = !initiatedFetchPresenceSocket.current ? JSON.parse(e.data) : [...tradePresenceTxns.current, ...JSON.parse(e.data)] - initiatedFetchPresenceSocket.current = true - processOffersWithPresence() - restarted = false - } + tradePresenceTxns.current = !initiatedFetchPresenceSocket.current + ? JSON.parse(e.data) + : [...tradePresenceTxns.current, ...JSON.parse(e.data)]; + initiatedFetchPresenceSocket.current = true; + processOffersWithPresence(); + restarted = false; + }; socketPresenceRef.current.onclose = (event) => { - clearTimeout(socketTimeout) - if (event.reason === 'forced') { - return + clearTimeout(socketTimeout); + if (event.reason === "forced") { + return; } - restartTradePresenceWebSocket() - } + restartTradePresenceWebSocket(); + }; socketPresenceRef.current.onerror = (e) => { - clearTimeout(socketTimeout) - restartTradePresenceWebSocket() - } + clearTimeout(socketTimeout); + restartTradePresenceWebSocket(); + }; const pingSocket = () => { - socketPresenceRef.current.send('ping') - socketTimeout = setTimeout(pingSocket, 295000) - } - } - - - + socketPresenceRef.current.send("ping"); + socketTimeout = setTimeout(pingSocket, 295000); + }; + }; const initTradeOffersWebSocket = (restarted = false) => { + if (socketRef.current) return; + let socketTimeout: any; - if(socketRef.current) return - let socketTimeout: any - - let socketLink - if(isUsingGateway){ - socketLink = `wss://appnode.qortal.org/websockets/crosschain/tradeoffers?foreignBlockchain=${selectedCoin}&includeHistoric=true` + let socketLink; + if (isUsingGateway) { + socketLink = `wss://appnode.qortal.org/websockets/crosschain/tradeoffers?foreignBlockchain=${selectedCoin}&includeHistoric=true`; } else { - socketLink = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${baseLocalHost}/websockets/crosschain/tradeoffers?foreignBlockchain=${selectedCoin}&includeHistoric=true` - + socketLink = `${ + window.location.protocol === "https:" ? "wss:" : "ws:" + }//${baseLocalHost}/websockets/crosschain/tradeoffers?foreignBlockchain=${selectedCoin}&includeHistoric=true`; } - socketRef.current = new WebSocket(socketLink) + socketRef.current = new WebSocket(socketLink); socketRef.current.onopen = () => { - setTimeout(pingSocket, 50) - } + setTimeout(pingSocket, 50); + }; socketRef.current.onmessage = (e) => { - offeringTrades.current = [...offeringTrades.current?.filter((coin)=> coin?.foreignBlockchain === selectedCoin), ...JSON.parse(e.data)?.filter((coin)=> coin?.foreignBlockchain === selectedCoin)] - restarted = false - processOffersWithPresence() - } + offeringTrades.current = [ + ...offeringTrades.current?.filter( + (coin) => coin?.foreignBlockchain === selectedCoin + ), + ...JSON.parse(e.data)?.filter( + (coin) => coin?.foreignBlockchain === selectedCoin + ), + ]; + restarted = false; + processOffersWithPresence(); + }; socketRef.current.onclose = (event) => { - clearTimeout(socketTimeout) - if (event.reason === 'forced') { - return + clearTimeout(socketTimeout); + if (event.reason === "forced") { + return; } - restartTradeOffersWebSocket() - socketRef.current = null - } + restartTradeOffersWebSocket(); + socketRef.current = null; + }; socketRef.current.onerror = (e) => { - clearTimeout(socketTimeout) - } + clearTimeout(socketTimeout); + }; const pingSocket = () => { - socketRef.current.send('ping') - socketTimeout = setTimeout(pingSocket, 295000) - } - } - + socketRef.current.send("ping"); + socketTimeout = setTimeout(pingSocket, 295000); + }; + }; useEffect(() => { - blockedTradesList.current = JSON.parse(localStorage.getItem('failedTrades') || '[]') - if(!initiatedFetchPresence.current){ - initiatedFetchPresence.current = true - initTradePresenceWebSocket() - + blockedTradesList.current = JSON.parse( + localStorage.getItem("failedTrades") || "[]" + ); + if (!initiatedFetchPresence.current) { + initiatedFetchPresence.current = true; + initTradePresenceWebSocket(); } - getNewBlockedTrades() + getNewBlockedTrades(); const intervalBlockTrades = setInterval(() => { - - initiatedFetchPresenceSocket.current = false - restartPresence() - initTradePresenceWebSocket() - getNewBlockedTrades() - }, 150000) + initiatedFetchPresenceSocket.current = false; + restartPresence(); + initTradePresenceWebSocket(); + getNewBlockedTrades(); + }, 150000); return () => { - clearInterval(intervalBlockTrades) - } - }, [isUsingGateway]) - - + clearInterval(intervalBlockTrades); + }; + }, [isUsingGateway]); useEffect(() => { - if(selectedCoin === null) return - restartTradeOffers() + if (selectedCoin === null) return; + restartTradeOffers(); setTimeout(() => { - initTradeOffersWebSocket() - + initTradeOffersWebSocket(); }, 500); return () => { - if(socketRef.current){ - socketRef.current.close(1000, 'forced'); + if (socketRef.current) { + socketRef.current.close(1000, "forced"); } - } - }, [isUsingGateway, selectedCoin]) - - - - + }; + }, [isUsingGateway, selectedCoin]); const selectedTotalLTC = useMemo(() => { return selectedOffers.reduce((acc: number, curr: any) => { @@ -409,7 +496,6 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { }, 0); }, [selectedOffers]); - const buyOrder = async () => { try { if(+foreignCoinBalance < +selectedTotalLTC.toFixed(4)){ @@ -420,35 +506,50 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { }) return } - - if (selectedOffers?.length < 1) return - setOpen(true) - setInfo({ - type: 'info', - message: "Attempting to submit buy order. Please wait..." - }) - const listOfATs = selectedOffers - const response = await qortalRequestWithTimeout({ - action: "CREATE_TRADE_BUY_ORDER", - crosschainAtInfo: listOfATs, - foreignBlockchain: selectedCoin - }, 900000); - - if(response?.error){ - setOpen(true) - setInfo({ - type: 'error', - message: response?.error || "Failed to submit trade order." - }) - return + + if (selectedOffers?.length < 1) return; + + setIsShowBuyInProgress({ status: "buying" }); + + // setOpen(true) + // setInfo({ + // type: 'info', + // message: "Attempting to submit buy order. Please wait..." + // }) + const listOfATs = selectedOffers; + const response = await qortalRequestWithTimeout( + { + action: "CREATE_TRADE_BUY_ORDER", + crosschainAtInfo: listOfATs, + foreignBlockchain: selectedCoin, + }, + 900000 + ); + + if (response?.error) { + setIsShowBuyInProgress({ + status: "error", + message: response?.error || "Failed to submit trade order.", + }); + // setOpen(true) + // setInfo({ + // type: 'error', + // message: response?.error || "Failed to submit trade order." + // }) + return; } if (response?.extra?.atAddresses) { - setSelectedOffers([]) + setIsShowBuyInProgress({ status: "success" }); + setSelectedOffers([]); const transactionData = { qortalAtAddresses: response?.extra?.atAddresses, qortAddress: response?.extra?.senderAddress, node: response?.extra?.node, - status:response?.extra?.status ? response?.extra?.status : response.callResponse === true ? 'trade-ongoing' : 'trade-failed', + status: response?.extra?.status + ? response?.extra?.status + : response.callResponse === true + ? "trade-ongoing" + : "trade-failed", encryptedMessageToBase58: response?.encryptedMessageToBase58, chatSignature: response?.chatSignature, sender: response?.extra?.senderAddress, @@ -456,43 +557,44 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { reference: response?.callResponse?.reference, }; - - // Update transactions in IndexedDB const result = await updateTransactionInDB(transactionData); - setOpen(true) + setOpen(true); setInfo({ - type: 'success', - message: "Submitted Order" - }) - fetchOngoingTransactions() - if(isUsingGateway){ - setRecord(transactionData) + type: "success", + message: "Submitted Order", + }); + fetchOngoingTransactions(); + if (isUsingGateway) { + setRecord(transactionData); await showInfo({ message: `Keep a record of your order in case your trade gets stuck`, - }) + }); } - } - } catch (error) { - setOpen(true) - setInfo({ - type: 'error', - message: error?.error || error?.message || "Failed to submit trade order." - }) - console.error(error) + setIsShowBuyInProgress({ + status: "error", + message: + error?.error || error?.message || "Failed to submit trade order.", + }); + // setOpen(true) + // setInfo({ + // type: 'error', + // message: error?.error || error?.message || "Failed to submit trade order." + // }) + // console.error(error) } - } + }; - - const getRowStyle = (params: RowClassParams): RowStyle | undefined => { - + const getRowStyle = ( + params: RowClassParams + ): RowStyle | undefined => { if (listOfOngoingTradesAts.includes(params.data.qortalAtAddress)) { - return { background: '#D9D9D91A'}; + return { background: "#D9D9D91A" }; } if (params.data.qortalAtAddress === selectedOffer?.qortalAtAddress) { - return { background: '#6D94F533'}; + return { background: "#6D94F533" }; } return undefined; }; @@ -503,146 +605,171 @@ export const TradeOffers: React.FC = ({foreignCoinBalance}:any) => { const onSelectionChanged = (event: any) => { const selectedRows = event.api.getSelectedRows(); - + setSelectedOffers([...selectedRows]); // Set all selected rows }; - + const onRowClicked = (event: any) => { if (listOfOngoingTradesAts.includes(event.data.qortalAtAddress)) return; const selectedRows = gridRef.current?.api.getSelectedRows(); setSelectedOffers([...selectedRows]); // Always spread the array to ensure state updates correctly }; - const updateGridData = (newData: any) => { if (gridRef.current) { - setOffers(newData); - } }; const getRowId = useCallback(function (params: any) { return String(params.data.qortalAtAddress); -}, []); + }, []); -const selectedTotalQORT = useMemo(() => { - return selectedOffers.reduce((acc: number, curr: any) => { - return acc + (+curr.qortAmount || 0); // Ensure qortAmount is defined - }, 0); -}, [selectedOffers]); + const selectedTotalQORT = useMemo(() => { + return selectedOffers.reduce((acc: number, curr: any) => { + return acc + (+curr.qortAmount || 0); // Ensure qortAmount is defined + }, 0); + }, [selectedOffers]); + const onGridReady = useCallback((params: any) => { + params.api.sizeColumnsToFit(); // Adjust columns to fit the grid width + const allColumnIds = params.columnApi + .getAllColumns() + .map((col: any) => col.getColId()); + params.columnApi.autoSizeColumns(allColumnIds); // Automatically adjust the width to fit content + }, []); -const onGridReady = useCallback((params: any) => { - params.api.sizeColumnsToFit(); // Adjust columns to fit the grid width - const allColumnIds = params.columnApi.getAllColumns().map((col: any) => col.getColId()); - params.columnApi.autoSizeColumns(allColumnIds); // Automatically adjust the width to fit content -}, []); + const handleClose = ( + event?: React.SyntheticEvent | Event, + reason?: SnackbarCloseReason + ) => { + if (reason === "clickaway") { + return; + } - -const handleClose = ( - event?: React.SyntheticEvent | Event, - reason?: SnackbarCloseReason, -) => { - if (reason === 'clickaway') { - return; - } - - setOpen(false); - setInfo(null) -}; - - - + setOpen(false); + setInfo(null); + }; return ( - -
- params.data.qortalAtAddress} // Ensure rows have unique IDs - /> - {/* {selectedOffer && ( + +
+ params.data.qortalAtAddress} // Ensure rows have unique IDs + /> + {/* {selectedOffer && ( )} */} -
-
- - - {selectedTotalQORT?.toFixed(3)} QORT - - foreignCoinBalance ? 'red' : 'white', - }}>{selectedTotalLTC?.toFixed(4)} {`${getCoinLabel()} `} - - +
+
+ + + + {selectedTotalQORT?.toFixed(3)} QORT + + + foreignCoinBalance ? "red" : "white", + }} + > + {selectedTotalLTC?.toFixed(4)}{" "} + {`${getCoinLabel()} `} + + + + {foreignCoinBalance?.toFixed(4)}{" "} + + {`${getCoinLabel()} `} balance + + - {foreignCoinBalance?.toFixed(4)} {`${getCoinLabel()} `} balance + {BuyButton()} - {BuyButton()} - - + {info?.message} @@ -655,35 +782,167 @@ const handleClose = ( > {"Download record"} - + {messageInfo.message} - + - )} + {isShowBuyInProgress && ( + + + {isShowBuyInProgress?.status === "error" && ( + + + + {` Failed to submit buy order.`} + + + {isShowBuyInProgress?.message} + + )} + {isShowBuyInProgress?.status === "success" && ( + + + + {` Successfully submitted order.`} + + + + You can see the progress of your + order in the "Pending" table. + + + + Note: Submission of an order does not necessarily mean that + your submission will be the one completing the order. Another + account may have submitted it before you. + + + )} + {isShowBuyInProgress?.status === "buying" && ( + + + + {` Attempting to submit buy order`} + + + + Please do not leave this application until there is a + response. Please wait! + + + {isUsingGateway && ( + <> + + Using gateway: might take up to 3 minutes to submit the buy order. + + + + { + //nothing + }} + size={60} + strokeWidth={4} + > + {({ remainingTime }) => {remainingTime}} + + + + )} + + )} + + + + + + )} ); }; - diff --git a/src/components/sell/CreateSell.tsx b/src/components/sell/CreateSell.tsx index 66f3f14..d4e6c7a 100644 --- a/src/components/sell/CreateSell.tsx +++ b/src/components/sell/CreateSell.tsx @@ -232,7 +232,7 @@ export const CreateSell = ({qortAddress, show}) => { /> - {`Price Each (${getCoinLabel()})`} + {`Price of Each QORT (in ${getCoinLabel()})`} { {`${qortAmount * foreignAmount} ${getCoinLabel()}`} for {qortAmount} QORT Total sell amount needs to be greater than: {minimumAmountSellTrades[selectedCoin]?.value} {' '} {minimumAmountSellTrades[selectedCoin]?.ticker} From 37099e90c8f040791fb52ab82f69cb6c2fea5fd6 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 25 Dec 2024 10:42:48 +0200 Subject: [PATCH 10/25] added history table --- src/components/history/History.tsx | 110 ++++++++++++++ src/components/history/HistoryList.tsx | 190 +++++++++++++++++++++++++ src/components/sell/TradeBotList.tsx | 42 +----- src/pages/Home/Home.tsx | 4 +- src/utils/formatTime.ts | 19 +++ 5 files changed, 323 insertions(+), 42 deletions(-) create mode 100644 src/components/history/History.tsx create mode 100644 src/components/history/HistoryList.tsx diff --git a/src/components/history/History.tsx b/src/components/history/History.tsx new file mode 100644 index 0000000..6cb056d --- /dev/null +++ b/src/components/history/History.tsx @@ -0,0 +1,110 @@ +import { Alert, Box, Button, ButtonBase, DialogActions, DialogContent, DialogTitle, IconButton, InputLabel, Snackbar, SnackbarCloseReason, TextField, Typography, styled } from '@mui/material' +import React, { useCallback, useContext, useEffect, useMemo, 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 HistoryList from './HistoryList'; +import RefreshIcon from "@mui/icons-material/Refresh"; + + + + +export const History = ({qortAddress, show}) => { + const [buyHistory, setBuyHistory] = useState({}) + const [sellHistory, setSellHistory] = useState({}) + + const { getCoinLabel, selectedCoin} = useContext(gameContext) + const [mode, setMode] = useState('buyHistory') + const [open, setOpen] = useState(false) + + const selectedHistory = useMemo(()=> { + if(mode === 'buyHistory') return buyHistory[selectedCoin] || [] + if(mode === 'sellHistory') return sellHistory[selectedCoin] || [] + }, [selectedCoin, buyHistory, sellHistory, mode]) + const getBuyHistory = useCallback((address, foreignBlockchain, mode, limit = 20)=> { + setOpen(true) + let historyUrl + if(mode === 'buyHistory'){ + historyUrl = `/crosschain/trades?foreignBlockchain=${foreignBlockchain}&buyerAddress=${address}&limit=${limit}&reverse=true`; + + } + if(mode === 'sellHistory'){ + historyUrl = `/crosschain/trades?foreignBlockchain=${foreignBlockchain}&sellerAddress=${address}&limit=${limit}&reverse=true`; + + } + + + + fetch(historyUrl) + .then((response) => { + return response.json(); + }) + .then((data) => { + if(mode === 'buyHistory'){ + setBuyHistory((prev)=> { + return { + ...prev, + [foreignBlockchain]: data + } + }) + } + if(mode === 'sellHistory'){ + setSellHistory((prev)=> { + return { + ...prev, + [foreignBlockchain]: data + } + }) + } + + }).catch(()=> {}).finally(()=> { + setOpen(false) + }) + }, []) + + useEffect(()=> { + if(!qortAddress || !selectedCoin) return + if(mode === 'buyHistory' && buyHistory[selectedCoin])return + if(mode === 'sellHistory' && sellHistory[selectedCoin])return + + getBuyHistory(qortAddress, selectedCoin, mode) + }, [qortAddress, selectedCoin, buyHistory, mode]) + + return ( +
+ + + { + getBuyHistory(qortAddress, selectedCoin, mode) + }}> + + + Showing most recent 20 results + + { + setOpen(false) + }} + > + setOpen(false)} + severity="info" + variant="filled" + sx={{ width: "100%" }} + > + {'Fetching History'} + + +
+ ) +} diff --git a/src/components/history/HistoryList.tsx b/src/components/history/HistoryList.tsx new file mode 100644 index 0000000..dd680a3 --- /dev/null +++ b/src/components/history/HistoryList.tsx @@ -0,0 +1,190 @@ +import { ColDef } from "ag-grid-community"; +import { AgGridReact } from "ag-grid-react"; +import React, { + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { autoSizeStrategy, baseLocalHost } from "../Grids/TradeOffers"; +import { Alert, Box, Snackbar, SnackbarCloseReason, Typography } from "@mui/material"; +import gameContext from "../../contexts/gameContext"; +import { formatTimestampForum } from "../../utils/formatTime"; + +const defaultColDef = { + resizable: true, // Make columns resizable by default + sortable: true, // Make columns sortable by default + suppressMovable: true, // Prevent columns from being movable +}; + + + +export default function HistoryList({ qortAddress, historyList }) { + const gridRef = useRef(null); + console.log('historyList', historyList) + const { getCoinLabel, selectedCoin} = useContext(gameContext) + const [qortalNames, setQortalNames] = useState({}); + + + const onGridReady = useCallback((params: any) => { + params.api.sizeColumnsToFit(); // Adjust columns to fit the grid width + const allColumnIds = params.columnApi + .getAllColumns() + .map((col: any) => col.getColId()); + params.columnApi.autoSizeColumns(allColumnIds); // Automatically adjust the width to fit content + }, []); + + const getName = async (address) => { + try { + const response = await fetch("/names/address/" + address); + const nameData = await response.json(); + if (nameData?.length > 0) { + setQortalNames((prev) => { + return { + ...prev, + [address]: nameData[0].name, + }; + }); + } else { + setQortalNames((prev) => { + return { + ...prev, + [address]: null, + }; + }); + } + } catch (error) { + // error + } + }; + + + const columnDefs: ColDef[] = useMemo(()=> { + return [ + { + headerName: "QORT AMOUNT", + field: "qortAmount", + flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: `${getCoinLabel()}/QORT`, + valueGetter: (params) => + +params.data.foreignAmount / +params.data.qortAmount, + sortable: true, + flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: `Total ${getCoinLabel()} Value`, + field: "foreignAmount", + flex: 1, // Flex makes this column responsive + minWidth: 150, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: "Time", + field: "tradeTimestamp", + valueGetter: (params) => + formatTimestampForum(params.data.tradeTimestamp), + flex: 1, // Flex makes this column responsive + minWidth: 200, // Ensure it doesn't shrink too much + resizable: true, + }, + { + headerName: "Buyer", + field: "buyerReceivingAddress", + flex: 1, // Flex makes this column responsive + minWidth: 200, // Ensure it doesn't shrink too much + resizable: true, + valueGetter: (params) => { + if (params?.data?.buyerReceivingAddress) { + if (qortalNames[params?.data?.buyerReceivingAddress]) { + return qortalNames[params?.data?.buyerReceivingAddress]; + } else if (qortalNames[params?.data?.buyerReceivingAddress] === undefined) { + getName(params?.data?.buyerReceivingAddress); + + return params?.data?.buyerReceivingAddress; + } else { + return params?.data?.buyerReceivingAddress; + } + } + }, + }, + { + headerName: "Seller", + field: "sellerAddress", + flex: 1, // Flex makes this column responsive + minWidth: 200, // Ensure it doesn't shrink too much + resizable: true, + valueGetter: (params) => { + if (params?.data?.sellerAddress) { + if (qortalNames[params?.data?.sellerAddress]) { + return qortalNames[params?.data?.sellerAddress]; + } else if (qortalNames[params?.data?.sellerAddress] === undefined) { + getName(params?.data?.sellerAddress); + + return params?.data?.sellerAddress; + } else { + return params?.data?.sellerAddress; + } + } + }, + }, + ]; + + }, [selectedCoin, qortalNames]) + + + + // const onSelectionChanged = (event: any) => { + // const selectedRows = event.api.getSelectedRows(); + // if(selectedRows[0]){ + // setSelectedTrade(selectedRows[0]) + // } else { + // setSelectedTrade(null) + // } + // }; + + + + + + + + return ( +
+
+ params.data.qortalAtAddress} // Ensure rows have unique IDs + /> +
+
+ ); +} diff --git a/src/components/sell/TradeBotList.tsx b/src/components/sell/TradeBotList.tsx index c317868..c619787 100644 --- a/src/components/sell/TradeBotList.tsx +++ b/src/components/sell/TradeBotList.tsx @@ -18,47 +18,7 @@ const defaultColDef = { suppressMovable: true, // Prevent columns from being movable }; -// const columnDefs: ColDef[] = [ -// { -// headerCheckboxSelection: false, // Adds a checkbox in the header for selecting all rows -// checkboxSelection: true, // Adds checkboxes in each row for selection -// headerName: "Select", // You can customize the header name -// width: 50, // Adjust the width as needed -// pinned: "left", // Optional, to pin this column on the left -// resizable: false, -// }, -// { -// headerName: "QORT AMOUNT", -// field: "qortAmount", -// flex: 1, // Flex makes this column responsive -// minWidth: 150, // Ensure it doesn't shrink too much -// resizable: true, -// }, -// { -// headerName: "LTC/QORT", -// valueGetter: (params) => -// +params.data.foreignAmount / +params.data.qortAmount, -// sortable: true, -// sort: "asc", -// flex: 1, // Flex makes this column responsive -// minWidth: 150, // Ensure it doesn't shrink too much -// resizable: true, -// }, -// { -// headerName: "Total LTC Value", -// field: "foreignAmount", -// flex: 1, // Flex makes this column responsive -// minWidth: 150, // Ensure it doesn't shrink too much -// resizable: true, -// }, -// { -// headerName: "Status", -// field: "status", -// flex: 1, // Flex makes this column responsive -// minWidth: 300, // Ensure it doesn't shrink too much -// resizable: true, -// }, -// ]; + export default function TradeBotList({ qortAddress, failedTradeBots }) { const [tradeBotList, setTradeBotList] = useState([]); diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index bdac02b..4a75776 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -16,6 +16,7 @@ import { Spacer } from "../../components/common/Spacer"; import { ReusableModal } from "../../components/common/reusable-modal/ReusableModal"; import { OAuthButton, OAuthButtonRow } from "./Home-Styles"; import { CreateSell } from "../../components/sell/CreateSell"; +import { History } from "../../components/history/History"; interface IsInstalledProps {} @@ -34,7 +35,7 @@ export const HomePage: FC = ({}) => { selectedCoin } = useContext(gameContext); const { setNotification } = useContext(NotificationContext); - const [mode, setMode] = useState("buy"); + const [mode, setMode] = useState("history"); const filteredOngoingTrades = useMemo(()=> { return onGoingTrades?.filter((item)=> item?.tradeInfo?.foreignBlockchain === selectedCoin) }, [onGoingTrades, selectedCoin]) @@ -108,6 +109,7 @@ export const HomePage: FC = ({}) => {
+ ); diff --git a/src/utils/formatTime.ts b/src/utils/formatTime.ts index bc26d72..6bc2c01 100644 --- a/src/utils/formatTime.ts +++ b/src/utils/formatTime.ts @@ -1,6 +1,25 @@ +import moment from "moment"; + export function formatTime(seconds: number): string { const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; // Pad the seconds with a leading zero if less than 10 return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`; } + + +export function formatTimestampForum(timestamp: number): string { + const now = moment(); + const timestampMoment = moment(timestamp); + const elapsedTime = now.diff(timestampMoment, 'minutes'); + + if (elapsedTime < 1) { + return `Just now - ${timestampMoment.format('h:mm A')}`; + } else if (elapsedTime < 60) { + return `${elapsedTime}m ago - ${timestampMoment.format('h:mm A')}`; + } else if (elapsedTime < 1440) { + return `${Math.floor(elapsedTime / 60)}h ago - ${timestampMoment.format('h:mm A')}`; + } else { + return timestampMoment.format('MMM D, YYYY - h:mm A'); + } +} \ No newline at end of file From 3de91a32d3ae7afded0cc9da1f03b0fd1a692c40 Mon Sep 17 00:00:00 2001 From: Justin Ferrari <‘justinwesleyferrari@gmail.com’> Date: Wed, 25 Dec 2024 15:08:04 -0500 Subject: [PATCH 11/25] More styling changes --- .../reusable-modal/ReusableModal-styles.tsx | 23 +- .../common/reusable-modal/ReusableModal.tsx | 14 +- src/components/header/Header-styles.tsx | 122 ++++++++++- src/components/header/Header.tsx | 176 ++++++++++++++- src/components/history/History-styles.tsx | 57 +++++ src/components/history/History.tsx | 201 ++++++++++-------- src/pages/Home/Home.tsx | 1 - 7 files changed, 484 insertions(+), 110 deletions(-) create mode 100644 src/components/history/History-styles.tsx diff --git a/src/components/common/reusable-modal/ReusableModal-styles.tsx b/src/components/common/reusable-modal/ReusableModal-styles.tsx index c9358b4..8ed0f36 100644 --- a/src/components/common/reusable-modal/ReusableModal-styles.tsx +++ b/src/components/common/reusable-modal/ReusableModal-styles.tsx @@ -1,5 +1,6 @@ import { Box, Button } from "@mui/material"; import { styled } from "@mui/system"; +import CloseIcon from '@mui/icons-material/Close'; export const ReusableModalContainer = styled(Box)(({ theme }) => ({ display: "flex", @@ -21,15 +22,6 @@ export const ReusableModalContainer = styled(Box)(({ theme }) => ({ "0px 4px 5px 0px hsla(0,0%,0%,0.14), \n\t\t0px 1px 10px 0px hsla(0,0%,0%,0.12), \n\t\t0px 2px 4px -1px hsla(0,0%,0%,0.2)", })); -export const ReusableModalSubContainer = styled(Box)({ - display: "flex", - flexDirection: "column", - alignItems: "center", - justifyContent: "center", - gap: "20px", - padding: "70px", -}); - export const ReusableModalBackdrop = styled(Box)({ position: "fixed", top: "0", @@ -50,4 +42,17 @@ export const ReusableModalButton = styled(Button)(({ theme }) => ({ border: `1px solid ${theme.palette.text.primary}`, color: theme.palette.text.primary, boxShadow: "1px 4px 10.5px 0px #0000004D" +})); + +export const ReusableModalCloseIcon = styled(CloseIcon)(({ theme }) => ({ + color: theme.palette.text.primary, + cursor: "pointer", + fontSize: "30px", + position: "absolute", + top: "20px", + right: "20px", + transition: "all 0.3s ease-in-out", + "&:hover": { + transform: "scale(1.1)", + }, })); \ No newline at end of file diff --git a/src/components/common/reusable-modal/ReusableModal.tsx b/src/components/common/reusable-modal/ReusableModal.tsx index d2b6d74..877b136 100644 --- a/src/components/common/reusable-modal/ReusableModal.tsx +++ b/src/components/common/reusable-modal/ReusableModal.tsx @@ -1,20 +1,26 @@ import { ReusableModalBackdrop, + ReusableModalCloseIcon, ReusableModalContainer, - ReusableModalSubContainer, } from "./ReusableModal-styles"; interface ReusableModalProps { backdrop?: boolean; + onClickClose: () => void; children: React.ReactNode; } -export const ReusableModal: React.FC = ({ backdrop, children }) => { +export const ReusableModal: React.FC = ({ + backdrop, + children, + onClickClose, +}) => { return ( <> - {children} + + {children} - {backdrop && } + {backdrop && } ); }; diff --git a/src/components/header/Header-styles.tsx b/src/components/header/Header-styles.tsx index de67e5b..7778779 100644 --- a/src/components/header/Header-styles.tsx +++ b/src/components/header/Header-styles.tsx @@ -1,5 +1,5 @@ import { styled } from "@mui/system"; -import { Box, Button, Typography } from "@mui/material"; +import { Box, Button, Typography, Theme, TextField } from "@mui/material"; import { HomeSVG } from "../common/icons/HomeSVG"; import { QortalLogoSVG } from "../common/icons/QortalLogoSVG"; import { CaretDownSVG } from "../common/icons/CaretDownSVG"; @@ -217,3 +217,123 @@ export const CoinSelectRow = styled(Box)({ gap: "5px", alignSelf: "flex-start", }); + +export const CoinActionContainer = styled(Box)({ + display: "flex", + flexDirection: "column", + gap: "25px", + alignItems: "center", + justifyContent: "center", + width: "80%", +}); + +export const CoinActionRow = styled(Box)({ + display: "flex", + flexDirection: "row", + width: "100%", + alignItems: "center", + justifyContent: "center", +}); + +export const HeaderRow = styled(Box)({ + display: "flex", + flexDirection: "row", + gap: "10px", + alignItems: "center", + justifyContent: "center", +}); + +export const SendFont = styled(Typography)(({ theme }) => ({ + fontFamily: "Inter", + color: theme.palette.text.primary, + fontWeight: 400, + fontSize: "18px", + lineHeight: "25px", + userSelect: "none", +})); + +const customInputStyle = (theme: Theme) => { + return { + fontFamily: "Inter", + fontSize: "18px", + fontWeight: 300, + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.default, + borderColor: theme.palette.background.paper, + "& label": { + color: theme.palette.mode === "light" ? "#808183" : "#edeef0", + fontFamily: "Inter", + fontSize: "18px", + letterSpacing: "0px", + }, + "& label.Mui-focused": { + color: theme.palette.mode === "light" ? "#A0AAB4" : "#d7d8da", + }, + "& .MuiInput-underline:after": { + borderBottomColor: theme.palette.mode === "light" ? "#B2BAC2" : "#c9cccf", + }, + "& .MuiOutlinedInput-root": { + "& fieldset": { + borderColor: "#E0E3E7", + }, + "&:hover fieldset": { + borderColor: "#B2BAC2", + }, + "&.Mui-focused fieldset": { + borderColor: "#6F7E8C", + }, + }, + "& .MuiInputBase-root": { + fontFamily: "Inter", + fontSize: "18px", + letterSpacing: "0px", + }, + "& .MuiFilledInput-root:after": { + borderBottomColor: theme.palette.secondary.main, + }, + }; +}; + +export const CustomInputField = styled(TextField)(({ theme }) => + customInputStyle(theme as Theme) +); + +export const CoinCancelBtn = styled(Button)({ + backgroundColor: "transparent", + color: "#d62525", + border: "1px solid #d62525", + fontFamily: "Inter, sans-serif", + fontWeight: 500, + fontSize: "14px", + height: "32px", + width: "80px", + lineHeight: "16px", + padding: "5px 10px", + borderRadius: "0px", + transition: "all 0.3s ease-in-out", + "&:hover": { + border: "1px solid #d62525", + backgroundColor: "#d62525", + color: "#000000" + }, +}); + +export const CoinConfirmSendBtn = styled(Button)(({ theme }) => ({ + backgroundColor: theme.palette.primary.main, + color: "#000000", + border: `1px solid ${theme.palette.primary.main}`, + fontFamily: "Inter, sans-serif", + fontWeight: 500, + fontSize: "14px", + height: "32px", + width: "80px", + lineHeight: "16px", + padding: "5px 10px", + borderRadius: "0px", + transition: "all 0.3s ease-in-out", + "&:hover": { + border: `1px solid ${theme.palette.text.primary}`, + color: "#000000", + backgroundColor: theme.palette.text.primary, + }, +})); \ No newline at end of file diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index f4e5515..3fa5edc 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -1,15 +1,22 @@ import { useState, useEffect, useRef, useContext, ChangeEvent } from "react"; import { BubbleCardColored1, + CoinActionContainer, + CoinActionRow, CoinActionsRow, + CoinCancelBtn, + CoinConfirmSendBtn, CoinReceiveBtn, CoinSelectRow, CoinSendBtn, + CustomInputField, HeaderNav, + HeaderRow, HeaderText, LogoColumn, NameRow, RightColumn, + SendFont, TotalCol, Username, } from "./Header-styles"; @@ -25,6 +32,7 @@ import { Box, Card, CardContent, + FormControl, FormControlLabel, MenuItem, Select, @@ -42,6 +50,8 @@ import rvnIcon from "../../assets/img/rvn.png"; import dgbIcon from "../../assets/img/dgb.png"; import arrrIcon from "../../assets/img/arrr.png"; import { Spacer } from "../common/Spacer"; +import { ReusableModal } from "../common/reusable-modal/ReusableModal"; +import { NotificationContext } from "../../contexts/notificationContext"; const checkIfLocal = async () => { try { @@ -65,6 +75,11 @@ export const Label = styled("label")( ` ); +interface CoinModalProps { + coin: string; + type: string; +} + const getCoinIcon = (coin) => { let img; @@ -126,6 +141,11 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { const [checked, setChecked] = useState(false); const [open, setOpen] = useState(false); const [info, setInfo] = useState(null); + const [openCoinActionModal, setOpenCoinActionModal] = + useState(null); + const [receiverAddress, setReceiverAddress] = useState(""); + const [senderAddress, setSenderAddress] = useState(""); + const { isUsingGateway } = useContext(gameContext); const handleChange = (event: ChangeEvent) => { @@ -138,7 +158,7 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { }; const { userInfo, selectedCoin, setSelectedCoin, getCoinLabel } = useContext(gameContext); - const { avatar, setAvatar } = useContext(UserContext); + const { setNotification } = useContext(NotificationContext); const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ padding: 8, @@ -327,8 +347,26 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { {qortBalance} QORT
- Send - Receive + { + setOpenCoinActionModal({ + coin: "QORT", + type: "send", + }); + }} + > + Send + + { + setOpenCoinActionModal({ + coin: "QORT", + type: "receive", + }); + }} + > + Receive + @@ -351,8 +389,26 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { {getCoinLabel()} - Send - Receive + { + setOpenCoinActionModal({ + coin: selectedCoin, + type: "send", + }); + }} + > + Send + + { + setOpenCoinActionModal({ + coin: selectedCoin, + type: "receive", + }); + }} + > + Receive + @@ -400,6 +456,116 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { {info?.message} + {openCoinActionModal && ( + { + setOpenCoinActionModal(null); + }} + backdrop + > + + + + {openCoinActionModal.type === "send" && + openCoinActionModal.coin === "QORT" ? ( + <> + Send {openCoinActionModal.coin} + + + ) : openCoinActionModal.type === "send" && + openCoinActionModal.coin !== "QORT" ? ( + <> + Send {openCoinActionModal.coin} + + + ) : openCoinActionModal.type === "receive" && + openCoinActionModal.coin === "QORT" ? ( + <> + Receive {openCoinActionModal.coin} + + + ) : openCoinActionModal.type === "receive" && + openCoinActionModal.coin !== "QORT" ? ( + <> + Receive {openCoinActionModal.coin} + + + ) : null} + + + + + { + if (openCoinActionModal.type === "send") { + setSenderAddress(e.target.value); + } else { + setReceiverAddress(e.target.value); + } + }} + /> + + + + setOpenCoinActionModal(null)}> + Cancel + + { + setNotification({ + alertType: "alertInfo", + msg: "Sending...", + }); + }} + > + {openCoinActionModal.type === "send" ? "Send" : "Receive"} + + + + + )} ); diff --git a/src/components/history/History-styles.tsx b/src/components/history/History-styles.tsx new file mode 100644 index 0000000..7950943 --- /dev/null +++ b/src/components/history/History-styles.tsx @@ -0,0 +1,57 @@ +import { Box, styled } from "@mui/system"; +import { Button, Typography } from "@mui/material"; +import RefreshIcon from "@mui/icons-material/Refresh"; + +type HistoryBtnProp = { + activeBtn: boolean; +}; + +export const HistoryButtonRow = styled(Box)({ + display: "flex", + alignItems: "center", + gap: "5px", + margin: "5px 5px 5px 0", +}); + +export const HistoryButton = styled(Button, { + shouldForwardProp: (prop) => prop !== "activeBtn", +})(({ theme, activeBtn }) => ({ + fontFamily: "Inter", + color: activeBtn ? theme.palette.text.primary : theme.palette.primary.main, + fontWeight: 400, + fontSize: "16px", + height: "30px", + lineHeight: "40px", + userSelect: "none", + background: activeBtn ? theme.palette.primary.main : "transparent", + border: `1px solid ${theme.palette.primary.main}`, + transition: "all 0.3s ease-in-out", + "&:hover": { + border: `1px solid ${theme.palette.primary.main}`, + background: theme.palette.primary.main, + color: theme.palette.text.primary, + cursor: "pointer", + }, +})); + +export const Refresh = styled(RefreshIcon)({ + cursor: "pointer", + color: "#fff", + fontSize: "25px", + marginLeft: "5px", + transition: "all 0.3s ease-in-out", + "&:hover": { + cursor: "pointer", + transform: "scale(1.1)", + }, +}); + +export const ShowingFont = styled(Typography)(({ theme }) => ({ + fontFamily: "Inter", + color: theme.palette.text.primary, + fontWeight: 400, + fontSize: "16px", + lineHeight: "25px", + marginBottom: "5px", + userSelect: "none", +})); diff --git a/src/components/history/History.tsx b/src/components/history/History.tsx index 6cb056d..cf039e9 100644 --- a/src/components/history/History.tsx +++ b/src/components/history/History.tsx @@ -1,110 +1,131 @@ -import { Alert, Box, Button, ButtonBase, DialogActions, DialogContent, DialogTitle, IconButton, InputLabel, Snackbar, SnackbarCloseReason, TextField, Typography, styled } from '@mui/material' -import React, { useCallback, useContext, useEffect, useMemo, 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 HistoryList from './HistoryList'; -import RefreshIcon from "@mui/icons-material/Refresh"; +import { + Alert, + Box, + ButtonBase, + Snackbar +} from "@mui/material"; +import React, { + useCallback, + useContext, + useEffect, + useMemo, + useState, +} from "react"; +import gameContext from "../../contexts/gameContext"; +import HistoryList from "./HistoryList"; +import { ShowingFont, Refresh, HistoryButtonRow, HistoryButton } from "./History-styles"; +export const History = ({ qortAddress, show }) => { + const [buyHistory, setBuyHistory] = useState({}); + const [sellHistory, setSellHistory] = useState({}); - + const { selectedCoin } = useContext(gameContext); + const [mode, setMode] = useState("buyHistory"); + const [open, setOpen] = useState(false); -export const History = ({qortAddress, show}) => { - const [buyHistory, setBuyHistory] = useState({}) - const [sellHistory, setSellHistory] = useState({}) + const selectedHistory = useMemo(() => { + if (mode === "buyHistory") return buyHistory[selectedCoin] || []; + if (mode === "sellHistory") return sellHistory[selectedCoin] || []; + }, [selectedCoin, buyHistory, sellHistory, mode]); + const getBuyHistory = useCallback( + (address, foreignBlockchain, mode, limit = 20) => { + setOpen(true); + let historyUrl; + if (mode === "buyHistory") { + historyUrl = `/crosschain/trades?foreignBlockchain=${foreignBlockchain}&buyerAddress=${address}&limit=${limit}&reverse=true`; + } + if (mode === "sellHistory") { + historyUrl = `/crosschain/trades?foreignBlockchain=${foreignBlockchain}&sellerAddress=${address}&limit=${limit}&reverse=true`; + } - const { getCoinLabel, selectedCoin} = useContext(gameContext) - const [mode, setMode] = useState('buyHistory') - const [open, setOpen] = useState(false) + fetch(historyUrl) + .then((response) => { + return response.json(); + }) + .then((data) => { + if (mode === "buyHistory") { + setBuyHistory((prev) => { + return { + ...prev, + [foreignBlockchain]: data, + }; + }); + } + if (mode === "sellHistory") { + setSellHistory((prev) => { + return { + ...prev, + [foreignBlockchain]: data, + }; + }); + } + }) + .catch(() => {}) + .finally(() => { + setOpen(false); + }); + }, + [] + ); - const selectedHistory = useMemo(()=> { - if(mode === 'buyHistory') return buyHistory[selectedCoin] || [] - if(mode === 'sellHistory') return sellHistory[selectedCoin] || [] - }, [selectedCoin, buyHistory, sellHistory, mode]) - const getBuyHistory = useCallback((address, foreignBlockchain, mode, limit = 20)=> { - setOpen(true) - let historyUrl - if(mode === 'buyHistory'){ - historyUrl = `/crosschain/trades?foreignBlockchain=${foreignBlockchain}&buyerAddress=${address}&limit=${limit}&reverse=true`; + useEffect(() => { + if (!qortAddress || !selectedCoin) return; + if (mode === "buyHistory" && buyHistory[selectedCoin]) return; + if (mode === "sellHistory" && sellHistory[selectedCoin]) return; - } - if(mode === 'sellHistory'){ - historyUrl = `/crosschain/trades?foreignBlockchain=${foreignBlockchain}&sellerAddress=${address}&limit=${limit}&reverse=true`; + getBuyHistory(qortAddress, selectedCoin, mode); + }, [qortAddress, selectedCoin, buyHistory, mode]); - } - - - - fetch(historyUrl) - .then((response) => { - return response.json(); - }) - .then((data) => { - if(mode === 'buyHistory'){ - setBuyHistory((prev)=> { - return { - ...prev, - [foreignBlockchain]: data - } - }) - } - if(mode === 'sellHistory'){ - setSellHistory((prev)=> { - return { - ...prev, - [foreignBlockchain]: data - } - }) - } - - }).catch(()=> {}).finally(()=> { - setOpen(false) - }) - }, []) - - useEffect(()=> { - if(!qortAddress || !selectedCoin) return - if(mode === 'buyHistory' && buyHistory[selectedCoin])return - if(mode === 'sellHistory' && sellHistory[selectedCoin])return - - getBuyHistory(qortAddress, selectedCoin, mode) - }, [qortAddress, selectedCoin, buyHistory, mode]) - return ( -
- - - { - getBuyHistory(qortAddress, selectedCoin, mode) - }}> - + + + { + setMode("buyHistory"); + }} + > + Buy History + + { + setMode("sellHistory"); + }} + > + Sell History + + { + getBuyHistory(qortAddress, selectedCoin, mode); + }} + > + - Showing most recent 20 results - - + Showing most recent 20 results + + { - setOpen(false) + onClose={() => { + setOpen(false); }} > setOpen(false)} + onClose={() => setOpen(false)} severity="info" variant="filled" sx={{ width: "100%" }} > - {'Fetching History'} + {"Fetching History"} -
- ) -} + + ); +}; diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index ccb1cda..69fdc30 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -8,7 +8,6 @@ import { OngoingTrades } from "../../components/Grids/OngoingTrades"; import { Box } from "@mui/material"; import { TextTableTitle } from "../../components/Grids/Table-styles"; import { Spacer } from "../../components/common/Spacer"; -import { ReusableModal } from "../../components/common/reusable-modal/ReusableModal"; import { Tab, TabDivider, TabsContainer, TabsRow } from "./Home-Styles"; import { CreateSell } from "../../components/sell/CreateSell"; import { History } from "../../components/history/History"; From 1350ef44c6266b0c5a3e89f9d81b6b7d5a34b0dc Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 26 Dec 2024 05:47:01 +0200 Subject: [PATCH 12/25] send and receiving foreign coins --- package-lock.json | 44 +++++ package.json | 2 + src/App.tsx | 2 +- src/assets/SVG/Copy.svg | 3 + src/components/header/AddressQRCode.tsx | 50 +++++ src/components/header/Header-styles.tsx | 1 + src/components/header/Header.tsx | 235 +++++++++++++++++++++--- src/contexts/gameContext.ts | 2 +- src/pages/Home/Home.tsx | 2 +- 9 files changed, 317 insertions(+), 24 deletions(-) create mode 100644 src/assets/SVG/Copy.svg create mode 100644 src/components/header/AddressQRCode.tsx diff --git a/package-lock.json b/package-lock.json index d6a4b93..95abc3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,10 +19,12 @@ "lodash": "^4.17.21", "moment": "^2.30.1", "react": "^18.2.0", + "react-copy-to-clipboard": "^5.1.0", "react-countdown-circle-timer": "^3.2.1", "react-dom": "^18.2.0", "react-ga4": "^2.1.0", "react-loader-spinner": "^6.1.6", + "react-qr-code": "^2.0.15", "react-router-dom": "^6.23.0", "react-toastify": "^10.0.5", "sass": "^1.76.0", @@ -3967,6 +3969,14 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/core-js-compat": { "version": "3.38.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", @@ -6328,6 +6338,11 @@ "node": ">=6" } }, + "node_modules/qr.js": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", + "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6368,6 +6383,18 @@ "node": ">=0.10.0" } }, + "node_modules/react-copy-to-clipboard": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", + "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==", + "dependencies": { + "copy-to-clipboard": "^3.3.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^15.3.0 || 16 || 17 || 18" + } + }, "node_modules/react-countdown-circle-timer": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/react-countdown-circle-timer/-/react-countdown-circle-timer-3.2.1.tgz", @@ -6414,6 +6441,18 @@ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-qr-code": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.15.tgz", + "integrity": "sha512-MkZcjEXqVKqXEIMVE0mbcGgDpkfSdd8zhuzXEl9QzYeNcw8Hq2oVIzDLWuZN2PQBwM5PWjc2S31K8Q1UbcFMfw==", + "dependencies": { + "prop-types": "^15.8.1", + "qr.js": "0.0.0" + }, + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -7286,6 +7325,11 @@ "node": ">=8.0" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, "node_modules/tr46": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", diff --git a/package.json b/package.json index f23f0d3..77087a4 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,12 @@ "lodash": "^4.17.21", "moment": "^2.30.1", "react": "^18.2.0", + "react-copy-to-clipboard": "^5.1.0", "react-countdown-circle-timer": "^3.2.1", "react-dom": "^18.2.0", "react-ga4": "^2.1.0", "react-loader-spinner": "^6.1.6", + "react-qr-code": "^2.0.15", "react-router-dom": "^6.23.0", "react-toastify": "^10.0.5", "sass": "^1.76.0", diff --git a/src/App.tsx b/src/App.tsx index 13fa6bb..a99a84e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -256,7 +256,7 @@ function App() { }; }, [userInfo?.address]); - const getCoinLabel = (coin?: string)=> { + const getCoinLabel = (coin?: string)=> { switch(coin || selectedCoin){ case "LITECOIN":{ diff --git a/src/assets/SVG/Copy.svg b/src/assets/SVG/Copy.svg new file mode 100644 index 0000000..0348fc9 --- /dev/null +++ b/src/assets/SVG/Copy.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/header/AddressQRCode.tsx b/src/components/header/AddressQRCode.tsx new file mode 100644 index 0000000..f4858fb --- /dev/null +++ b/src/components/header/AddressQRCode.tsx @@ -0,0 +1,50 @@ +import React, { useState } from "react"; +import QRCode from "react-qr-code"; +import { Box, Typography } from "@mui/material"; + +export const AddressQRCode = ({ targetAddress }) => { + return ( + + + + + + + + + + + + ); +}; diff --git a/src/components/header/Header-styles.tsx b/src/components/header/Header-styles.tsx index 7778779..b0898f7 100644 --- a/src/components/header/Header-styles.tsx +++ b/src/components/header/Header-styles.tsx @@ -216,6 +216,7 @@ export const CoinSelectRow = styled(Box)({ flexDirection: "column", gap: "5px", alignSelf: "flex-start", + marginBottom: '5px' }); export const CoinActionContainer = styled(Box)({ diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index 3fa5edc..4d8c57f 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef, useContext, ChangeEvent } from "react"; +import { useState, useEffect, useRef, useContext, ChangeEvent, useMemo } from "react"; import { BubbleCardColored1, CoinActionContainer, @@ -25,6 +25,10 @@ import { UserContext } from "../../contexts/userContext"; import { cropAddress } from "../../utils/cropAddress"; import qtradeLogo from "../../components/common/icons/qtradeLogo.png"; import qortIcon from "../../assets/img/qort.png"; +import ErrorIcon from "@mui/icons-material/Error"; +import { CopyToClipboard } from "react-copy-to-clipboard"; +import Copy from "../../assets/SVG/Copy.svg"; +import {AddressQRCode} from './AddressQRCode' import { Alert, AppBar, @@ -39,6 +43,7 @@ import { Snackbar, SnackbarCloseReason, Switch, + Typography, styled, } from "@mui/material"; import { sendRequestToExtension } from "../../App"; @@ -145,7 +150,8 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { useState(null); const [receiverAddress, setReceiverAddress] = useState(""); const [senderAddress, setSenderAddress] = useState(""); - + const [amount, setAmount] = useState(""); + const [coinAddresses, setCoinAddresses] = useState({}); const { isUsingGateway } = useContext(gameContext); const handleChange = (event: ChangeEvent) => { @@ -160,6 +166,7 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { useContext(gameContext); const { setNotification } = useContext(NotificationContext); + const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ padding: 8, "& .MuiSwitch-track": { @@ -231,6 +238,40 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { // } // }, [userInfo]); + const sendCoin = async ()=> { + try { + const coin = getCoinLabel() + if(!coin) return + setOpen(true); + setInfo({ + type: "info", + message: "Sending Coin...", + autoHideDurationOff: true + }); + const response = await qortalRequest({ + action: "SEND_COIN", + coin, + destinationAddress: senderAddress, + amount: +amount + }); + if(response?.error){ + throw new Error(response?.error || "Failed to send coin.") + } + setOpen(true); + setInfo({ + type: "success", + message: "Coin sent", + }); + setAmount('') + } catch (error) { + setOpen(true); + setInfo({ + type: "error", + message: error?.error || error?.message, + }); + } + } + return ( <> { - { > {info?.message} + )} + {openCoinActionModal && ( { setOpenCoinActionModal(null); + setAmount('') + setSenderAddress('') }} backdrop > + {openCoinActionModal.type === "send" ? <> {openCoinActionModal.type === "send" && @@ -523,12 +570,12 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { style={{ flexGrow: 1 }} name={ openCoinActionModal.type === "send" - ? "Sender Address" + ? "Recipient Address" : "Receive Address" } label={ openCoinActionModal.type === "send" - ? "Sender Address" + ? "Recipient Address" : "Receive Address" } variant="filled" @@ -548,25 +595,171 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { /> - - setOpenCoinActionModal(null)}> - Cancel - - { - setNotification({ - alertType: "alertInfo", - msg: "Sending...", - }); - }} - > - {openCoinActionModal.type === "send" ? "Send" : "Receive"} - - + {openCoinActionModal.type === "send" && ( + + + { + setAmount(e.target.value) + }} + /> + + + )} + : ( + <> + + + )} + {openCoinActionModal.type === 'send' && ( + + setOpenCoinActionModal(null)}> + Cancel + + { + if(openCoinActionModal.type === 'send'){ + sendCoin() + } + setNotification({ + alertType: "alertInfo", + msg: "Sending...", + }); + }} + > + {openCoinActionModal.type === "send" ? "Send" : "Receive"} + + + )} + + + )} + ); }; + +export const AddressBox = styled(Box)` +display: flex; +border: 1px solid var(--50-white, rgba(255, 255, 255, 0.5)); +justify-content: space-between; +align-items: center; +width: auto; +word-break: break-word; +padding: 5px 15px 5px 15px; +gap: 5px; +border-radius: 100px; +font-family: Inter; +font-size: 12px; +font-weight: 600; +line-height: 14.52px; +text-align: left; +color: var(--50-white, rgba(255, 255, 255, 0.5)); +cursor: pointer; +transition: all 0.2s; +&:hover { + background-color: rgba(41, 41, 43, 1); + color: white; + svg path { + fill: white; // Fill color changes to white on hover + } + } + +` + + +const ReceiveCoin = ({coinAddresses, setCoinAddresses, selectedCoin, setOpen, setInfo})=> { + const [errorMsg, setErrorMsg] = useState('') + const foreignAddress = useMemo(()=> { + return coinAddresses[selectedCoin] || null + }, [coinAddresses, selectedCoin]) + + const getForeignAddress = async (coin)=> { + try { + setOpen(true); + setInfo({ + type: "info", + message: "Retrieving address...", + }); + const response = await qortalRequest({ + action: "GET_USER_WALLET", + coin + }); + if(response?.address){ + setCoinAddresses((prev)=> { + return { + ...prev, + [coin]: response.address + } + }) + } + if(response?.error){ + throw new Error(response?.error || "Failed to send coin.") + } + } catch (error) { + setErrorMsg(error?.message) + } finally { + setOpen(false); + setInfo(null); + } + } + + useEffect(()=> { + if(!selectedCoin) return + if(!coinAddresses[selectedCoin]){ + getForeignAddress(selectedCoin) + } + }, [selectedCoin, coinAddresses]) + + return ( + + {`Send ${selectedCoin} to your address below`} + + {foreignAddress && ( + { + setOpen(true); + setInfo({ + type: "info", + message: "Address copied!", + }); + }}> + + {foreignAddress} + + + )} + {foreignAddress && ( + <> + + + )} + {errorMsg && ( + <> + + {errorMsg} + + )} + + ) +} \ No newline at end of file diff --git a/src/contexts/gameContext.ts b/src/contexts/gameContext.ts index ab2d9dd..010b0cd 100644 --- a/src/contexts/gameContext.ts +++ b/src/contexts/gameContext.ts @@ -34,7 +34,7 @@ export interface IContextProps { isUsingGateway: boolean; selectedCoin: string; setSelectedCoin: (val: any)=> void; - getCoinLabel: ()=> void; + getCoinLabel: ()=> string | null; } const defaultState: IContextProps = { diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 69fdc30..0396f54 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -23,7 +23,7 @@ export const HomePage = () => { selectedCoin, } = useContext(gameContext); const { setNotification } = useContext(NotificationContext); - const [mode, setMode] = useState("history"); + const [mode, setMode] = useState("buy"); const filteredOngoingTrades = useMemo(() => { return onGoingTrades?.filter( (item) => item?.tradeInfo?.foreignBlockchain === selectedCoin From 375b9f3a6938e156b97118d63ad6e219b619d045 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 26 Dec 2024 05:55:36 +0200 Subject: [PATCH 13/25] fixes --- src/components/header/Header.tsx | 38 ++++++-------------------------- 1 file changed, 7 insertions(+), 31 deletions(-) diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index 4d8c57f..db738ca 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -240,7 +240,7 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { const sendCoin = async ()=> { try { - const coin = getCoinLabel() + const coin = openCoinActionModal.coin === "QORT" ? 'QORT' : getCoinLabel() if(!coin) return setOpen(true); setInfo({ @@ -537,31 +537,7 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { }} /> - ) : openCoinActionModal.type === "receive" && - openCoinActionModal.coin === "QORT" ? ( - <> - Receive {openCoinActionModal.coin} - - - ) : openCoinActionModal.type === "receive" && - openCoinActionModal.coin !== "QORT" ? ( - <> - Receive {openCoinActionModal.coin} - - - ) : null} + ) : null} @@ -570,12 +546,12 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { style={{ flexGrow: 1 }} name={ openCoinActionModal.type === "send" - ? "Recipient Address" + ? `${openCoinActionModal.coin === "QORT" ? 'Recipient Address or Name' : 'Recipient Address'}` : "Receive Address" } label={ openCoinActionModal.type === "send" - ? "Recipient Address" + ? `${openCoinActionModal.coin === "QORT" ? 'Recipient Address or Name' : 'Recipient Address'}` : "Receive Address" } variant="filled" @@ -617,14 +593,14 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { )} : ( <> - + )} {openCoinActionModal.type === 'send' && ( - setOpenCoinActionModal(null)}> + {/* setOpenCoinActionModal(null)}> Cancel - + */} { if(openCoinActionModal.type === 'send'){ From b73497aafd7fdd5394b25d82b903679aad4f80a4 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 26 Dec 2024 05:56:53 +0200 Subject: [PATCH 14/25] fixes --- src/contexts/gameContext.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contexts/gameContext.ts b/src/contexts/gameContext.ts index 010b0cd..dff3f1b 100644 --- a/src/contexts/gameContext.ts +++ b/src/contexts/gameContext.ts @@ -34,7 +34,7 @@ export interface IContextProps { isUsingGateway: boolean; selectedCoin: string; setSelectedCoin: (val: any)=> void; - getCoinLabel: ()=> string | null; + getCoinLabel: ()=> string | null | void; } const defaultState: IContextProps = { From 644909fbb043476083d3076464c1eeec6b1c4933 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 26 Dec 2024 05:59:28 +0200 Subject: [PATCH 15/25] added back base --- src/components/Grids/TradeOffers.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index 7a73c04..626de40 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -41,8 +41,8 @@ import CheckCircleIcon from "@mui/icons-material/CheckCircle"; import { CountdownCircleTimer } from "react-countdown-circle-timer"; import { BuyContainer } from "./Table-styles"; -// export const baseLocalHost = window.location.host -export const baseLocalHost = "127.0.0.1:12391"; +export const baseLocalHost = window.location.host +// export const baseLocalHost = "127.0.0.1:12391"; interface RowData { amountQORT: number; From 9652e081b1be40eea78869cb2f39cabf6906649a Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 26 Dec 2024 09:18:39 +0200 Subject: [PATCH 16/25] fixes --- src/App-styles.tsx | 5 ++++- src/components/Grids/TradeOffers.tsx | 2 +- src/components/header/Header.tsx | 10 +++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/App-styles.tsx b/src/App-styles.tsx index 68b87ab..81cd6f6 100644 --- a/src/App-styles.tsx +++ b/src/App-styles.tsx @@ -7,7 +7,10 @@ export const AppContainer = styled(Box)(({ theme }) => ({ flexDirection: "column", alignItems: "flex-start", padding: "20px 30px 0 30px", - backgroundColor: "#323336" + backgroundColor: "#323336", + [`@media (max-width: 500px)`]: { + padding: "10px 5px 0 5px", + } })); export const MainContainer = styled(Box)` diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index 626de40..b353bb7 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -795,7 +795,7 @@ export const TradeOffers: React.FC = ({ foreignCoinBalance }: any) => { width: "450px", height: "450px", maxHeight: "calc(90vh - 55px)", - maxWidth: "90vw", + maxWidth: "90%", background: "rgb(39, 40, 44)", overflow: "auto", }} diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index db738ca..2a6230c 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -29,6 +29,7 @@ import ErrorIcon from "@mui/icons-material/Error"; import { CopyToClipboard } from "react-copy-to-clipboard"; import Copy from "../../assets/SVG/Copy.svg"; import {AddressQRCode} from './AddressQRCode' +import {FallingLines} from 'react-loader-spinner' import { Alert, AppBar, @@ -426,7 +427,14 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { width: "auto", }} /> - {foreignCoinBalance === null ? "N/A" : foreignCoinBalance}{" "} + {foreignCoinBalance === null ? ( + + ) : foreignCoinBalance}{" "} {getCoinLabel()} From 98b6ef7a4de1ca68f5f3e3d562e16f148f25cbf7 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 26 Dec 2024 09:49:37 +0200 Subject: [PATCH 17/25] fix --- src/components/header/Header.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index 2a6230c..d22a16b 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -432,7 +432,6 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => { color="white" width="30" visible={true} - ariaLabel="falling-circles-loading" /> ) : foreignCoinBalance}{" "} {getCoinLabel()} From d84fcb06581e6851f13b5e9428640df8c058d4e5 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 26 Dec 2024 10:15:37 +0200 Subject: [PATCH 18/25] add zIndex --- src/components/Grids/Table-styles.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Grids/Table-styles.tsx b/src/components/Grids/Table-styles.tsx index 8597b9f..82ace53 100644 --- a/src/components/Grids/Table-styles.tsx +++ b/src/components/Grids/Table-styles.tsx @@ -20,4 +20,5 @@ export const BuyContainer = styled(Box)({ height: "100px", padding: "7px 14px", background: "#323336", + zIndex: 3 }); From aa3a13b3b20ca10f3eecb47258eccee8190be21b Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 26 Dec 2024 10:48:17 +0200 Subject: [PATCH 19/25] remove log --- src/components/history/HistoryList.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/history/HistoryList.tsx b/src/components/history/HistoryList.tsx index dd680a3..2d7cf06 100644 --- a/src/components/history/HistoryList.tsx +++ b/src/components/history/HistoryList.tsx @@ -23,7 +23,6 @@ const defaultColDef = { export default function HistoryList({ qortAddress, historyList }) { const gridRef = useRef(null); - console.log('historyList', historyList) const { getCoinLabel, selectedCoin} = useContext(gameContext) const [qortalNames, setQortalNames] = useState({}); From 837098d9db6ba3c82c01ef6077cf37853b6b37ad Mon Sep 17 00:00:00 2001 From: Justin Ferrari <‘justinwesleyferrari@gmail.com’> Date: Thu, 26 Dec 2024 16:50:00 -0500 Subject: [PATCH 20/25] Small UI Fixes --- src/components/header/Header-styles.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/header/Header-styles.tsx b/src/components/header/Header-styles.tsx index 7778779..d98d007 100644 --- a/src/components/header/Header-styles.tsx +++ b/src/components/header/Header-styles.tsx @@ -138,13 +138,14 @@ export const LogoColumn = styled(Box)({ gap: "10px", alignItems: "center", }); + export const RightColumn = styled(Box)({ display: "flex", flexDirection: "row", gap: "10px", alignItems: "flex-start", - padding: "10px", }); + export const AvatarCircle = styled("img")({ borderRadius: "50%", width: "35px", @@ -216,6 +217,7 @@ export const CoinSelectRow = styled(Box)({ flexDirection: "column", gap: "5px", alignSelf: "flex-start", + marginBottom: "5px" }); export const CoinActionContainer = styled(Box)({ @@ -314,7 +316,7 @@ export const CoinCancelBtn = styled(Button)({ "&:hover": { border: "1px solid #d62525", backgroundColor: "#d62525", - color: "#000000" + color: "#000000", }, }); @@ -336,4 +338,4 @@ export const CoinConfirmSendBtn = styled(Button)(({ theme }) => ({ color: "#000000", backgroundColor: theme.palette.text.primary, }, -})); \ No newline at end of file +})); From a52308d838428b428a8885aa9b443ada9c17bf70 Mon Sep 17 00:00:00 2001 From: Justin Ferrari <‘justinwesleyferrari@gmail.com’> Date: Thu, 26 Dec 2024 22:52:47 -0500 Subject: [PATCH 21/25] Fixed bottom UI buy container --- src/components/Grids/Table-styles.tsx | 42 ++- src/components/Grids/TradeOffers.tsx | 214 +++++++------- src/components/sell/CreateSell.tsx | 395 +++++++++++++++----------- src/components/sell/TradeBotList.tsx | 285 +++++++++++-------- src/pages/Home/Home-Styles.tsx | 2 +- src/pages/Home/Home.tsx | 8 +- 6 files changed, 544 insertions(+), 402 deletions(-) diff --git a/src/components/Grids/Table-styles.tsx b/src/components/Grids/Table-styles.tsx index 82ace53..d7dd8ac 100644 --- a/src/components/Grids/Table-styles.tsx +++ b/src/components/Grids/Table-styles.tsx @@ -1,5 +1,12 @@ 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 }) => ({ fontFamily: "Inter", @@ -11,14 +18,39 @@ export const TextTableTitle = styled(Typography)(({ theme }) => ({ })); export const BuyContainer = styled(Box)({ - width: "calc(100% - 60px)", + position: "fixed", + width: "calc(100% - 14px)", display: "flex", justifyContent: "space-between", alignItems: "center", - position: "fixed", bottom: "0px", height: "100px", - padding: "7px 14px", + padding: "18px 14px 12px 14px", 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", + } +})); diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index b353bb7..93d2a09 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -15,8 +15,6 @@ import { } from "ag-grid-community"; import "ag-grid-community/styles/ag-grid.css"; import "ag-grid-community/styles/ag-theme-alpine.css"; -import axios from "axios"; -import { sendRequestToExtension } from "../../App"; import { Alert, Box, @@ -39,9 +37,15 @@ import { Hourglass } from "react-loader-spinner"; import ErrorIcon from "@mui/icons-material/Error"; import CheckCircleIcon from "@mui/icons-material/CheckCircle"; 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"; interface RowData { @@ -110,23 +114,7 @@ export const TradeOffers: React.FC = ({ foreignCoinBalance }: any) => { const [open, setOpen] = useState(false); const [info, setInfo] = useState(null); const BuyButton = () => { - return ( - - ); + return BUY; }; const defaultColDef = { @@ -499,13 +487,13 @@ export const TradeOffers: React.FC = ({ foreignCoinBalance }: any) => { const buyOrder = async () => { try { - if(+foreignCoinBalance < +selectedTotalLTC.toFixed(4)){ - setOpen(true) + if (+foreignCoinBalance < +selectedTotalLTC.toFixed(4)) { + setOpen(true); setInfo({ - type: 'error', - message: `You don't have enough ${getCoinLabel()} or your balance was not retrieved` - }) - return + type: "error", + message: `You don't have enough ${getCoinLabel()} or your balance was not retrieved`, + }); + return; } if (selectedOffers?.length < 1) return; @@ -653,11 +641,7 @@ export const TradeOffers: React.FC = ({ foreignCoinBalance }: any) => { }; return ( - +
= ({ foreignCoinBalance }: any) => { )} */} -
-
- - - {selectedTotalQORT?.toFixed(3)} QORT - - foreignCoinBalance ? 'red' : 'white', - }}>{selectedTotalLTC?.toFixed(4)} {`${getCoinLabel()} `} - - +
+
+ + + + + {selectedTotalQORT?.toFixed(3)} QORT + + + foreignCoinBalance ? "red" : "white", + }} + > + {selectedTotalLTC?.toFixed(4)}{" "} + {`${getCoinLabel()} `} + + + + {foreignCoinBalance?.toFixed(4)}{" "} + + {`${getCoinLabel()} `} balance + + - {foreignCoinBalance?.toFixed(4)} {`${getCoinLabel()} `} balance - - {BuyButton()} - - + {BuyButton()} + + = ({ foreignCoinBalance }: any) => { - You can see the progress of your - order in the "Pending" table. + You can see the progress of your order in the "Pending" table. @@ -859,29 +870,34 @@ export const TradeOffers: React.FC = ({ foreignCoinBalance }: any) => { {isUsingGateway && ( <> - - Using gateway: might take up to 3 minutes to submit the buy order. - - - - { - //nothing - }} - size={60} - strokeWidth={4} - > - {({ remainingTime }) => {remainingTime}} - - + + Using gateway: might take up to 3 minutes to submit the + buy order. + + + + { + //nothing + }} + size={60} + strokeWidth={4} + > + {({ remainingTime }) => ( + {remainingTime} + )} + + )} @@ -905,6 +921,6 @@ export const TradeOffers: React.FC = ({ foreignCoinBalance }: any) => { )} - + ); }; diff --git a/src/components/sell/CreateSell.tsx b/src/components/sell/CreateSell.tsx index d4e6c7a..3c6e471 100644 --- a/src/components/sell/CreateSell.tsx +++ b/src/components/sell/CreateSell.tsx @@ -1,10 +1,24 @@ -import { Alert, Box, Button, DialogActions, DialogContent, 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'; +import { + Alert, + Box, + Button, + DialogActions, + DialogContent, + 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)` font-weight: 400; @@ -12,13 +26,12 @@ export const CustomLabel = styled(InputLabel)` font-size: 10px; line-height: 12px; color: rgba(255, 255, 255, 0.5); - -` +`; export const minimumAmountSellTrades = { - 'LITECOIN': { + LITECOIN: { value: 0.01, - ticker: 'LTC' + ticker: "LTC", }, DOGECOIN: { value: 1, @@ -40,168 +53,186 @@ export const minimumAmountSellTrades = { value: 0.0002, ticker: "ARRR", }, -} +}; export const CustomInput = styled(TextField)({ - width: "183px", // Adjust the width as needed - borderRadius: "5px", - // backgroundColor: "rgba(30, 30, 32, 1)", + width: "183px", // Adjust the width as needed + borderRadius: "5px", + // backgroundColor: "rgba(30, 30, 32, 1)", + outline: "none", + input: { + fontSize: 10, + fontFamily: "Inter", + fontWeight: 400, + color: "white", + "&::placeholder": { + fontSize: 16, + color: "rgba(255, 255, 255, 0.2)", + }, outline: "none", - input: { - fontSize: 10, - fontFamily: "Inter", - fontWeight: 400, - color: "white", - "&::placeholder": { - fontSize: 16, - color: "rgba(255, 255, 255, 0.2)", - }, - outline: "none", - padding: "10px", + padding: "10px", + }, + "& .MuiOutlinedInput-root": { + "& fieldset": { + border: "0.5px solid rgba(255, 255, 255, 0.5)", }, - "& .MuiOutlinedInput-root": { - "& fieldset": { - border: '0.5px solid rgba(255, 255, 255, 0.5)', - }, - "&:hover fieldset": { - border: '0.5px solid rgba(255, 255, 255, 0.5)', - }, - "&.Mui-focused fieldset": { - border: '0.5px solid rgba(255, 255, 255, 0.5)', - }, + "&:hover fieldset": { + border: "0.5px solid rgba(255, 255, 255, 0.5)", }, - "& .MuiInput-underline:before": { - borderBottom: "none", + "&.Mui-focused fieldset": { + border: "0.5px solid rgba(255, 255, 255, 0.5)", }, - "& .MuiInput-underline:hover:not(.Mui-disabled):before": { - borderBottom: "none", - }, - "& .MuiInput-underline:after": { - borderBottom: "none", - }, - }); - + }, + "& .MuiInput-underline:before": { + borderBottom: "none", + }, + "& .MuiInput-underline:hover:not(.Mui-disabled):before": { + borderBottom: "none", + }, + "& .MuiInput-underline:after": { + borderBottom: "none", + }, +}); -export const CreateSell = ({qortAddress, show}) => { - const [open, setOpen] = React.useState(false); - const [qortAmount, setQortAmount] = React.useState(0) - const [foreignAmount, setForeignAmount] = React.useState(0) - const {updateTemporaryFailedTradeBots, sellOrders, fetchTemporarySellOrders, isUsingGateway, getCoinLabel, selectedCoin} = useContext(gameContext) - const [openAlert, setOpenAlert] = React.useState(false) - const [info, setInfo] = React.useState(null) - const handleClickOpen = () => { - setOpen(true); - }; - const handleClose = () => { - setOpen(false); - setForeignAmount(0) - setQortAmount(0) - }; +export const CreateSell = ({ qortAddress, show }) => { + const [open, setOpen] = React.useState(false); + const [qortAmount, setQortAmount] = React.useState(0); + const [foreignAmount, setForeignAmount] = React.useState(0); + const { + updateTemporaryFailedTradeBots, + sellOrders, + fetchTemporarySellOrders, + isUsingGateway, + getCoinLabel, + selectedCoin, + } = useContext(gameContext); + const [openAlert, setOpenAlert] = React.useState(false); + const [info, setInfo] = React.useState(null); + const handleClickOpen = () => { + setOpen(true); + }; + const handleClose = () => { + setOpen(false); + setForeignAmount(0); + setQortAmount(0); + }; - const createSellOrder = async() => { - try { - setInfo({ - type: 'info', - message: "Attempting to create sell order. Please wait..." - }) - const res = await qortalRequestWithTimeout({ - action: "CREATE_TRADE_SELL_ORDER", - qortAmount, - foreignBlockchain: selectedCoin, - foreignAmount: qortAmount * foreignAmount - }, 900000); - - if(res?.error && res?.failedTradeBot){ - await updateTemporaryFailedTradeBots({ - atAddress: res?.failedTradeBot?.atAddress, - status: 'FAILED', - qortAddress: res?.failedTradeBot?.creatorAddress, - - }) - fetchTemporarySellOrders() - setOpenAlert(true) - setInfo({ - type: 'error', - message: "Unable to create sell order. Please try again." - }) - } - if(!res?.error){ - setOpenAlert(true) - setForeignAmount(0) - setQortAmount(0) - setOpen(false) + const createSellOrder = async () => { + try { + setInfo({ + type: "info", + message: "Attempting to create sell order. Please wait...", + }); + const res = await qortalRequestWithTimeout( + { + action: "CREATE_TRADE_SELL_ORDER", + qortAmount, + foreignBlockchain: selectedCoin, + foreignAmount: qortAmount * foreignAmount, + }, + 900000 + ); - setInfo({ - type: 'success', - message: "Sell order created. Please wait a couple of minutes for the network to propogate the changes." - }) - } - } catch (error) { - if(error?.error && error?.failedTradeBot){ - await updateTemporaryFailedTradeBots({ - atAddress: error?.failedTradeBot?.atAddress, - status: 'FAILED', - qortAddress: error?.failedTradeBot?.creatorAddress, - - }) - fetchTemporarySellOrders() - setOpenAlert(true) - setInfo({ - type: 'error', - message: "Unable to create sell order. Please try again." - }) - } - } - } - - const handleCloseAlert = ( - event?: React.SyntheticEvent | Event, - reason?: SnackbarCloseReason, - ) => { - if (reason === 'clickaway') { - return; + if (res?.error && res?.failedTradeBot) { + await updateTemporaryFailedTradeBots({ + atAddress: res?.failedTradeBot?.atAddress, + status: "FAILED", + qortAddress: res?.failedTradeBot?.creatorAddress, + }); + fetchTemporarySellOrders(); + setOpenAlert(true); + setInfo({ + type: "error", + message: "Unable to create sell order. Please try again.", + }); } - - setOpenAlert(false); - setInfo(null) - }; - - if(isUsingGateway){ + if (!res?.error) { + setOpenAlert(true); + setForeignAmount(0); + setQortAmount(0); + setOpen(false); - return ( -
- - Managing your sell orders is not possible using a gateway node. Please switch to a local or custom node at the authentication page - -
- ) + setInfo({ + type: "success", + message: + "Sell order created. Please wait a couple of minutes for the network to propogate the changes.", + }); + } + } catch (error) { + if (error?.error && error?.failedTradeBot) { + await updateTemporaryFailedTradeBots({ + atAddress: error?.failedTradeBot?.atAddress, + status: "FAILED", + qortAddress: error?.failedTradeBot?.creatorAddress, + }); + fetchTemporarySellOrders(); + setOpenAlert(true); + setInfo({ + type: "error", + message: "Unable to create sell order. Please try again.", + }); + } } - return ( -
- - item.status === 'FAILED')} /> + }; - { + if (reason === "clickaway") { + return; + } + + setOpenAlert(false); + setInfo(null); + }; + + if (isUsingGateway) { + return ( +
+ + Managing your sell orders is not possible using a gateway node. Please + switch to a local or custom node at the authentication page + +
+ ); + } + return ( +
+ + item.status === "FAILED")} + /> + + @@ -211,7 +242,7 @@ export const CreateSell = ({qortAddress, show}) => { aria-label="close" onClick={handleClose} sx={(theme) => ({ - position: 'absolute', + position: "absolute", right: 8, top: 8, color: theme.palette.grey[500], @@ -220,8 +251,10 @@ export const CreateSell = ({qortAddress, show}) => { - - QORT amount + + + QORT amount + { autoComplete="off" /> - {`${qortAmount * foreignAmount} ${getCoinLabel()}`} for {qortAmount} QORT - Total sell amount needs to be greater than: {minimumAmountSellTrades[selectedCoin]?.value} {' '} {minimumAmountSellTrades[selectedCoin]?.ticker} + + {`${qortAmount * foreignAmount} ${getCoinLabel()}`} for{" "} + {qortAmount} QORT + + + Total sell amount needs to be greater than:{" "} + {minimumAmountSellTrades[selectedCoin]?.value}{" "} + {minimumAmountSellTrades[selectedCoin]?.ticker} + - - - + {info?.message}
- ) -} + ); +}; diff --git a/src/components/sell/TradeBotList.tsx b/src/components/sell/TradeBotList.tsx index c619787..20f6378 100644 --- a/src/components/sell/TradeBotList.tsx +++ b/src/components/sell/TradeBotList.tsx @@ -9,8 +9,15 @@ import React, { useState, } from "react"; 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 { BuyContainerDivider } from "../Grids/Table-styles"; const defaultColDef = { resizable: true, // Make columns resizable by default @@ -18,18 +25,22 @@ const defaultColDef = { suppressMovable: true, // Prevent columns from being movable }; - - export default function TradeBotList({ qortAddress, failedTradeBots }) { const [tradeBotList, setTradeBotList] = useState([]); const [selectedTrade, setSelectedTrade] = useState(null); - const tradeBotListRef = useRef([]) + const tradeBotListRef = useRef([]); const offeringTrades = useRef([]); const qortAddressRef = useRef(null); const gridRef = useRef(null); - const {updateTemporaryFailedTradeBots, fetchTemporarySellOrders, deleteTemporarySellOrder, getCoinLabel, selectedCoin} = useContext(gameContext) - const [open, setOpen] = useState(false) - const [info, setInfo] = useState(null) + const { + updateTemporaryFailedTradeBots, + fetchTemporarySellOrders, + deleteTemporarySellOrder, + getCoinLabel, + selectedCoin, + } = useContext(gameContext); + const [open, setOpen] = useState(false); + const [info, setInfo] = useState(null); const filteredOutTradeBotListWithoutFailed = useMemo(() => { const list = tradeBotList.filter( (item) => @@ -37,7 +48,7 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { (failedItem) => failedItem.atAddress === item.atAddress ) ); - return list + return list; }, [failedTradeBots, tradeBotList]); const onGridReady = useCallback((params: any) => { @@ -48,8 +59,7 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { params.columnApi.autoSizeColumns(allColumnIds); // Automatically adjust the width to fit content }, []); - - const columnDefs: ColDef[] = useMemo(()=> { + const columnDefs: ColDef[] = useMemo(() => { return [ { headerCheckboxSelection: false, // Adds a checkbox in the header for selecting all rows @@ -91,8 +101,7 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { resizable: true, }, ]; - - }, [selectedCoin]) + }, [selectedCoin]); useEffect(() => { if (qortAddress) { qortAddressRef.current = qortAddress; @@ -136,7 +145,7 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { const processTradeBots = (tradeBots) => { let sellTrades = [...tradeBotListRef.current]; // Start with the existing trades - + tradeBots.forEach((trade) => { const status = processTradeBotState(trade); @@ -145,7 +154,7 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { const existingIndex = sellTrades.findIndex( (existingTrade) => existingTrade.atAddress === trade.atAddress ); - + if (existingIndex > -1) { // Replace the existing trade if it exists sellTrades[existingIndex] = { ...trade, status }; @@ -159,23 +168,25 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { tradeBotListRef.current = sellTrades; }; - const restartTradeOffers = ()=> { + const restartTradeOffers = () => { if (socketRef.current) { - socketRef.current.close(1000, 'forced'); // Close with a custom reason - socketRef.current = null + socketRef.current.close(1000, "forced"); // Close with a custom reason + socketRef.current = null; } - offeringTrades.current = [] + offeringTrades.current = []; setTradeBotList([]); tradeBotListRef.current = []; - } + }; - const socketRef = useRef(null) + const socketRef = useRef(null); const initTradeOffersWebSocket = (restarted = false) => { let tradeOffersSocketCounter = 0; let socketTimeout: any; // 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.onopen = () => { setTimeout(pingSocket, 50); @@ -188,8 +199,8 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { }; socketRef.current.onclose = (event) => { clearTimeout(socketTimeout); - if (event.reason === 'forced') { - return + if (event.reason === "forced") { + return; } restartTradeOffersWebSocket(); }; @@ -203,94 +214,113 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { }; useEffect(() => { - if(!qortAddress) return - if(selectedCoin === null) return - restartTradeOffers() - - setTimeout(() => { - initTradeOffersWebSocket() + if (!qortAddress) return; + if (selectedCoin === null) return; + restartTradeOffers(); + setTimeout(() => { + initTradeOffersWebSocket(); }, 500); - return () => { - if(socketRef.current){ - socketRef.current.close(1000, 'forced'); + return () => { + if (socketRef.current) { + socketRef.current.close(1000, "forced"); } - } + }; }, [qortAddress, selectedCoin]); const onSelectionChanged = (event: any) => { const selectedRows = event.api.getSelectedRows(); - if(selectedRows[0]){ - setSelectedTrade(selectedRows[0]) + if (selectedRows[0]) { + setSelectedTrade(selectedRows[0]); } else { - setSelectedTrade(null) + setSelectedTrade(null); } }; const handleClose = ( event?: React.SyntheticEvent | Event, - reason?: SnackbarCloseReason, + reason?: SnackbarCloseReason ) => { - if (reason === 'clickaway') { + if (reason === "clickaway") { return; } - + setOpen(false); - setInfo(null) + setInfo(null); }; - - - const cancelSell = async ()=> { + const cancelSell = async () => { try { - if(!selectedTrade) return - setOpen(true) + if (!selectedTrade) return; + setOpen(true); - setInfo({ - type: 'info', - message: "Attempting to cancel sell order" - }) - const res = await qortalRequestWithTimeout({ - action: "CANCEL_TRADE_SELL_ORDER", - qortAmount: selectedTrade.qortAmount, - foreignBlockchain: selectedTrade.foreignBlockchain, - foreignAmount: selectedTrade.foreignAmount, - atAddress: selectedTrade.atAddress - }, 900000); - if(res?.signature){ - await deleteTemporarySellOrder(selectedTrade.atAddress) - - - setSelectedTrade(null) - setOpen(true) - setInfo({ - type: 'success', - message: "Sell order canceled. Please wait a couple of minutes for the network to propogate the changes" - }) - } - if(res?.error && res?.failedTradeBot){ - setOpen(true) - setInfo({ - type: 'error', - message: "Unable to cancel sell order. Please try again." - }) - } - } catch (error) { - if(error?.error && error?.failedTradeBot){ - setOpen(true) setInfo({ - type: 'error', - message: "Unable to cancel sell order. Please try again." - }) - } - } - } + type: "info", + message: "Attempting to cancel sell order", + }); + const res = await qortalRequestWithTimeout( + { + action: "CANCEL_TRADE_SELL_ORDER", + qortAmount: selectedTrade.qortAmount, + foreignBlockchain: selectedTrade.foreignBlockchain, + foreignAmount: selectedTrade.foreignAmount, + atAddress: selectedTrade.atAddress, + }, + 900000 + ); + if (res?.signature) { + await deleteTemporarySellOrder(selectedTrade.atAddress); + + setSelectedTrade(null); + setOpen(true); + setInfo({ + type: "success", + message: + "Sell order canceled. Please wait a couple of minutes for the network to propogate the changes", + }); + } + if (res?.error && res?.failedTradeBot) { + setOpen(true); + setInfo({ + type: "error", + message: "Unable to cancel sell order. Please try again.", + }); + } + } catch (error) { + if (error?.error && error?.failedTradeBot) { + setOpen(true); + setInfo({ + type: "error", + message: "Unable to cancel sell order. Please try again.", + }); + } + } + }; const CancelButton = () => { return ( - ); @@ -329,62 +359,73 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { )} */}
- - - {/* + + + + {/* {selectedTotalQORT?.toFixed(3)} QORT */} - - {/* + {/* foreignCoinBalance ? 'red' : 'white', }}>{selectedTotalLTC?.toFixed(4)} LTC */} - - - - {/* + {/* {foreignCoinBalance?.toFixed(4)} LTC balance */} + + {CancelButton()} - {CancelButton()} - - + {info?.message} diff --git a/src/pages/Home/Home-Styles.tsx b/src/pages/Home/Home-Styles.tsx index c48d1dc..77890c1 100644 --- a/src/pages/Home/Home-Styles.tsx +++ b/src/pages/Home/Home-Styles.tsx @@ -69,7 +69,6 @@ export const HomeWrapper = styled(Box)({ export const TabsContainer = styled(Box)({ display: "flex", flexDirection: "column", - gap: "15px", alignItems: "flex-start", width: "100%", justifyContent: "center", @@ -78,6 +77,7 @@ export const TabsContainer = styled(Box)({ export const TabsRow = styled(Box)({ display: "flex", flexDirection: "row", + gap: "5px", justifyContent: "space-evenly", alignItems: "center", backgroundColor: "#323336", diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 0396f54..763ae18 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -58,17 +58,17 @@ export const HomePage = () => { setMode("buy")}> Buy QORT - + {/* */} setMode("sell")}> Sell QORT - - */} + {/* setMode("history")} > Trade History - + */}
Date: Thu, 26 Dec 2024 23:26:30 -0500 Subject: [PATCH 22/25] Two small mobile changes --- src/components/Grids/Table-styles.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/Grids/Table-styles.tsx b/src/components/Grids/Table-styles.tsx index d7dd8ac..b87a45b 100644 --- a/src/components/Grids/Table-styles.tsx +++ b/src/components/Grids/Table-styles.tsx @@ -30,7 +30,7 @@ export const BuyContainer = styled(Box)({ zIndex: 3, }); -export const BuyContainerDivider = styled(Box)({ +export const BuyContainerDivider = styled(Box)(({ theme }) => ({ position: "absolute", width: "60%", height: "1px", @@ -38,7 +38,10 @@ export const BuyContainerDivider = styled(Box)({ top: "10px", left: "50%", transform: "translateX(-50%)", -}); + [theme.breakpoints.down("sm")]: { + top: "5px", + } +})); export const BuyOrderBtn = styled("button")(({ theme }) => ({ borderRadius: "8px", @@ -51,6 +54,6 @@ export const BuyOrderBtn = styled("button")(({ theme }) => ({ boxShadow: "0px 2.77px 2.21px 0px #00000005", marginRight: "10px", [theme.breakpoints.down("sm")]: { - marginRight: "5px", + marginRight: "0px", } })); From 56f56dcae105416b39eee1e67170d79239fc4190 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 28 Dec 2024 20:20:08 +0200 Subject: [PATCH 23/25] remove unused var --- src/components/Grids/TradeOffers.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index 93d2a09..28dbbbc 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -41,7 +41,6 @@ import { BuyContainer, BuyContainerDivider, BuyOrderBtn, - ContentArea, MainContainer, } from "./Table-styles"; From 722338d3ea4c111f6b32ba7f219d9effb132226d Mon Sep 17 00:00:00 2001 From: Justin Ferrari <‘justinwesleyferrari@gmail.com’> Date: Sat, 28 Dec 2024 17:59:31 -0500 Subject: [PATCH 24/25] Fixed buy container width on Go --- src/components/Grids/Table-styles.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/Grids/Table-styles.tsx b/src/components/Grids/Table-styles.tsx index b87a45b..4ea70fa 100644 --- a/src/components/Grids/Table-styles.tsx +++ b/src/components/Grids/Table-styles.tsx @@ -17,7 +17,7 @@ export const TextTableTitle = styled(Typography)(({ theme }) => ({ userSelect: "none", })); -export const BuyContainer = styled(Box)({ +export const BuyContainer = styled(Box)(({ theme }) => ({ position: "fixed", width: "calc(100% - 14px)", display: "flex", @@ -28,7 +28,10 @@ export const BuyContainer = styled(Box)({ padding: "18px 14px 12px 14px", background: "#323336", zIndex: 3, -}); + [theme.breakpoints.down("sm")]: { + width: "calc(100% - 2px)", + } +})); export const BuyContainerDivider = styled(Box)(({ theme }) => ({ position: "absolute", From ae733da906f719ca93777498044aa64d7957f015 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Tue, 31 Dec 2024 21:23:21 +0200 Subject: [PATCH 25/25] add missing dep in usememo --- src/App.tsx | 6 +++--- src/components/Grids/TradeOffers.tsx | 2 +- src/components/history/HistoryList.tsx | 2 +- src/components/sell/TradeBotList.tsx | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index a99a84e..1acdd92 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import ReactGA from "react-ga4"; import "./App.css"; import socketService from "./services/socketService"; @@ -256,7 +256,7 @@ function App() { }; }, [userInfo?.address]); - const getCoinLabel = (coin?: string)=> { + const getCoinLabel = useCallback((coin?: string)=> { switch(coin || selectedCoin){ case "LITECOIN":{ @@ -285,7 +285,7 @@ function App() { default: return null } - } + }, [selectedCoin]) const gameContextValue: IContextProps = { userInfo, diff --git a/src/components/Grids/TradeOffers.tsx b/src/components/Grids/TradeOffers.tsx index 28dbbbc..aba4c44 100644 --- a/src/components/Grids/TradeOffers.tsx +++ b/src/components/Grids/TradeOffers.tsx @@ -218,7 +218,7 @@ export const TradeOffers: React.FC = ({ foreignCoinBalance }: any) => { }, }, ]; - }, [qortalNames]); + }, [qortalNames, getCoinLabel]); // const onRowClicked = (event: any) => { // if(listOfOngoingTradesAts.includes(event.data.qortalAtAddress)) return diff --git a/src/components/history/HistoryList.tsx b/src/components/history/HistoryList.tsx index 2d7cf06..4da4fd4 100644 --- a/src/components/history/HistoryList.tsx +++ b/src/components/history/HistoryList.tsx @@ -136,7 +136,7 @@ export default function HistoryList({ qortAddress, historyList }) { }, ]; - }, [selectedCoin, qortalNames]) + }, [selectedCoin, qortalNames, getCoinLabel]) diff --git a/src/components/sell/TradeBotList.tsx b/src/components/sell/TradeBotList.tsx index 20f6378..8b52fa1 100644 --- a/src/components/sell/TradeBotList.tsx +++ b/src/components/sell/TradeBotList.tsx @@ -101,7 +101,7 @@ export default function TradeBotList({ qortAddress, failedTradeBots }) { resizable: true, }, ]; - }, [selectedCoin]); + }, [selectedCoin, getCoinLabel]); useEffect(() => { if (qortAddress) { qortAddressRef.current = qortAddress;