This commit is contained in:
PhilReact 2025-05-04 20:31:43 +03:00
parent bbfd29e8fb
commit f32cd68554
8 changed files with 650 additions and 225 deletions

59
package-lock.json generated
View File

@ -19,7 +19,7 @@
"jotai": "^2.12.3",
"lodash": "^4.17.21",
"moment": "^2.30.1",
"qapp-core": "^1.0.24",
"qapp-core": "^1.0.25",
"react": "^19.1.0",
"react-countdown-circle-timer": "^3.2.1",
"react-dom": "^19.1.0",
@ -3128,6 +3128,7 @@
"version": "3.13.6",
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.6.tgz",
"integrity": "sha512-WT7nWs8ximoQ0CDx/ngoFP7HbQF9Q2wQe4nh2NB+u2486eX3nZRE40P9g6ccCVq7ZfTSH5gFOuCoVH5DLNS/aA==",
"license": "MIT",
"dependencies": {
"@tanstack/virtual-core": "3.13.6"
},
@ -3144,6 +3145,7 @@
"version": "3.13.6",
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.6.tgz",
"integrity": "sha512-cnQUeWnhNP8tJ4WsGcYiX24Gjkc9ALstLbHcBj1t3E7EimN6n6kHH+DPV4PpDnuw00NApQp+ViojMj1GRdwYQg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
@ -3254,7 +3256,8 @@
"node_modules/@types/seedrandom": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-3.0.8.tgz",
"integrity": "sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ=="
"integrity": "sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ==",
"license": "MIT"
},
"node_modules/@types/semver": {
"version": "7.5.8",
@ -3661,6 +3664,7 @@
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz",
"integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==",
"license": "MIT",
"engines": {
"node": ">=4"
}
@ -3762,6 +3766,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
@ -3783,7 +3788,8 @@
"type": "consulting",
"url": "https://feross.org/support"
}
]
],
"license": "MIT"
},
"node_modules/binary-extensions": {
"version": "2.3.0",
@ -3800,6 +3806,7 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/bloom-filters/-/bloom-filters-3.0.4.tgz",
"integrity": "sha512-BdnPWo2OpYhlvuP2fRzJBdioMCkm7Zp0HCf8NJgF5Mbyqy7VQ/CnTiVWMMyq4EZCBHwj0Kq6098gW2/3RsZsrA==",
"license": "MIT",
"dependencies": {
"@types/seedrandom": "^3.0.8",
"base64-arraybuffer": "^1.0.2",
@ -3817,7 +3824,8 @@
"node_modules/blueimp-canvas-to-blob": {
"version": "3.29.0",
"resolved": "https://registry.npmjs.org/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz",
"integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg=="
"integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==",
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "2.0.1",
@ -3889,6 +3897,7 @@
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
@ -4066,6 +4075,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/compressorjs/-/compressorjs-1.2.1.tgz",
"integrity": "sha512-+geIjeRnPhQ+LLvvA7wxBQE5ddeLU7pJ3FsKFWirDw6veY3s9iLxAQEw7lXGHnhCJvBujEQWuNnGzZcvCvdkLQ==",
"license": "MIT",
"dependencies": {
"blueimp-canvas-to-blob": "^3.29.0",
"is-blob": "^2.1.0"
@ -4128,7 +4138,8 @@
"node_modules/crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
"license": "MIT"
},
"node_modules/crypto-random-string": {
"version": "2.0.0",
@ -4167,7 +4178,8 @@
"node_modules/cuint": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz",
"integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw=="
"integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==",
"license": "MIT"
},
"node_modules/data-view-buffer": {
"version": "1.0.1",
@ -4223,7 +4235,8 @@
"node_modules/dayjs": {
"version": "1.11.13",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
"license": "MIT"
},
"node_modules/debug": {
"version": "4.3.7",
@ -4342,6 +4355,7 @@
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz",
"integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
@ -4944,6 +4958,7 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz",
"integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==",
"license": "MIT",
"dependencies": {
"tslib": "^2.7.0"
},
@ -4954,7 +4969,8 @@
"node_modules/file-selector/node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/filelist": {
"version": "1.0.4",
@ -5292,6 +5308,7 @@
"version": "2.1.16",
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz",
"integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==",
"license": "MIT",
"peerDependencies": {
"csstype": "^3.0.10"
}
@ -5435,7 +5452,8 @@
"type": "consulting",
"url": "https://feross.org/support"
}
]
],
"license": "BSD-3-Clause"
},
"node_modules/ignore": {
"version": "5.3.1",
@ -5553,6 +5571,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.1.0.tgz",
"integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==",
"license": "MIT",
"engines": {
"node": ">=6"
},
@ -5594,6 +5613,7 @@
"url": "https://feross.org/support"
}
],
"license": "MIT",
"engines": {
"node": ">=4"
}
@ -6162,7 +6182,8 @@
"node_modules/long": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
"license": "Apache-2.0"
},
"node_modules/loose-envify": {
"version": "1.4.0",
@ -6581,9 +6602,9 @@
}
},
"node_modules/qapp-core": {
"version": "1.0.24",
"resolved": "https://registry.npmjs.org/qapp-core/-/qapp-core-1.0.24.tgz",
"integrity": "sha512-KnrwiysaHlTR1rUnPGwN79gQgcjvtLr4xRH3EGGQcbXKCcbrklV7lv4KLUfFR4UwhskbgOXpJY5PECWdrlGXSw==",
"version": "1.0.25",
"resolved": "https://registry.npmjs.org/qapp-core/-/qapp-core-1.0.25.tgz",
"integrity": "sha512-JhdQliLPjapgzbCjkuSpR5hAk8ewT0FbQxIW+oxVWJyT1e0swAKXJqvX1PHQzTbiV5IQI12nbIxy6SjBam3K2A==",
"license": "MIT",
"dependencies": {
"@tanstack/react-virtual": "^3.13.2",
@ -6675,6 +6696,7 @@
"version": "14.3.8",
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.3.8.tgz",
"integrity": "sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==",
"license": "MIT",
"dependencies": {
"attr-accept": "^2.2.4",
"file-selector": "^2.1.0",
@ -6696,6 +6718,7 @@
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz",
"integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==",
"license": "MIT",
"dependencies": {
"csstype": "^3.1.3",
"goober": "^2.1.16"
@ -6712,6 +6735,7 @@
"version": "9.16.0",
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.16.0.tgz",
"integrity": "sha512-w9nJSEp+DrW9KmQmeWHQyfaP6b03v+TdXynaoA964Wxt7mdR3An11z4NNCQgL4gKSK7y1ver2Fq+JKH6CWEzUA==",
"license": "MIT",
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
@ -6836,7 +6860,8 @@
"node_modules/reflect-metadata": {
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz",
"integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A=="
"integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==",
"license": "Apache-2.0"
},
"node_modules/regenerate": {
"version": "1.4.2",
@ -7122,7 +7147,8 @@
"node_modules/seedrandom": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
"integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="
"integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==",
"license": "MIT"
},
"node_modules/semver": {
"version": "7.6.0",
@ -7906,6 +7932,7 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
@ -8434,6 +8461,7 @@
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz",
"integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==",
"license": "MIT",
"dependencies": {
"cuint": "^0.2.2"
}
@ -8468,6 +8496,7 @@
"version": "4.5.6",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz",
"integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==",
"license": "MIT",
"dependencies": {
"use-sync-external-store": "^1.2.2"
},

View File

@ -21,7 +21,7 @@
"jotai": "^2.12.3",
"lodash": "^4.17.21",
"moment": "^2.30.1",
"qapp-core": "^1.0.24",
"qapp-core": "^1.0.25",
"react": "^19.1.0",
"react-countdown-circle-timer": "^3.2.1",
"react-dom": "^19.1.0",

View File

@ -61,6 +61,7 @@ import CloseIcon from "@mui/icons-material/Close";
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
import moment from "moment";
import { RequestQueueWithPromise } from "qapp-core";
import { useUpdateFee } from "../../hooks/useUpdateFee";
const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text);
@ -91,6 +92,7 @@ export const autoSizeStrategy: SizeColumnsToContentStrategy = {
export const TradeOffers: React.FC<any> = ({
foreignCoinBalance,
fee,
setFee
}: any) => {
const [offers, setOffers] = useState<any[]>([]);
const [signedUnlockingFees, setSignedUnlockingFees] = useState(null);
@ -105,6 +107,7 @@ export const TradeOffers: React.FC<any> = ({
} = useContext(gameContext);
const isRemoveOrdersWithoutUnlockingFees = useRef(false);
const [isRemoveOrders, setIsRemoveOrders] = useState('remove');
const updateFee = useUpdateFee({setFee, selectedCoin})
const listOfOngoingTradesAts = useMemo(() => {
return (
onGoingTrades
@ -128,6 +131,14 @@ export const TradeOffers: React.FC<any> = ({
message: messageTradesUnknownFee,
} = useModal();
const {
isShow: isShowAskToUpdateFee,
onCancel: onCancelAskToUpdateFee,
onOk: onOkAskToUpdateFee,
show: showAskToUpdateFee,
message: messageAskToUpdateFee,
} = useModal();
const offersWithoutOngoing = useMemo(() => {
return offers.filter(
(item) => !listOfOngoingTradesAts.includes(item.qortalAtAddress)
@ -228,6 +239,20 @@ export const TradeOffers: React.FC<any> = ({
}
};
const rowTooltip = (params) => {
let selectable = true;
const hasSignedFee = signedUnlockingFees?.find(
(item) => item?.atAddress === params.data.qortalAtAddress
);
if (!hasSignedFee) selectable = true;
if (hasSignedFee && hasSignedFee?.fee > feeRef.current)
selectable = false;
if(!selectable)return 'Your unlocking fee is to low to buy this order, Increase your fee to purchase'
return ''
};
const columnDefs: ColDef[] = useMemo(() => {
return [
{
@ -239,7 +264,7 @@ export const TradeOffers: React.FC<any> = ({
pinned: "left", // Optional, to pin this column on the left
resizable: false,
suppressRowClickSelection: true,
tooltipValueGetter: rowTooltip,
cellRenderer: (params) => (
<SelectWithInfoCell
{...params}
@ -265,6 +290,7 @@ export const TradeOffers: React.FC<any> = ({
flex: 1, // Flex makes this column responsive
minWidth: 150, // Ensure it doesn't shrink too much
resizable: true,
tooltipValueGetter: rowTooltip
},
{
headerName: `${getCoinLabel()}/QORT`,
@ -275,6 +301,7 @@ export const TradeOffers: React.FC<any> = ({
flex: 1, // Flex makes this column responsive
minWidth: 150, // Ensure it doesn't shrink too much
resizable: true,
tooltipValueGetter: rowTooltip
},
{
headerName: `Total ${getCoinLabel()} Value`,
@ -282,12 +309,14 @@ export const TradeOffers: React.FC<any> = ({
flex: 1, // Flex makes this column responsive
minWidth: 150, // Ensure it doesn't shrink too much
resizable: true,
tooltipValueGetter: rowTooltip
},
{
headerName: `Unlocking fee`,
flex: 1, // Flex makes this column responsive
minWidth: 150, // Ensure it doesn't shrink too much
resizable: true,
tooltipValueGetter: rowTooltip,
valueGetter: (params) => {
if (params?.data?.qortalAtAddress) {
const hasSignedFee = signedUnlockingFees?.find(
@ -304,6 +333,7 @@ export const TradeOffers: React.FC<any> = ({
flex: 1, // Flex makes this column responsive
minWidth: 300, // Ensure it doesn't shrink too much
resizable: true,
tooltipValueGetter: rowTooltip,
valueGetter: (params) => {
if (params?.data?.qortalCreator) {
if (qortalNames[params?.data?.qortalCreator]) {
@ -660,6 +690,20 @@ export const TradeOffers: React.FC<any> = ({
offersWithKnownFees = selectedOffers;
}
const highestFee = offersWithKnownFees.length
? Math.max(
...offersWithKnownFees
.filter(o => typeof o.fee === 'number')
.map(o => o.fee as number)
)
: 0;
if(highestFee < fee){
await showAskToUpdateFee({
message: highestFee
})
}
setIsShowBuyInProgress({ status: "buying" });
@ -864,6 +908,7 @@ export const TradeOffers: React.FC<any> = ({
onGridReady={onGridReady}
// domLayout='autoHeight'
getRowId={(params) => params.data.qortalAtAddress} // Ensure rows have unique IDs
enableBrowserTooltips={true}
gridOptions={{
isRowSelectable: (params) => {
let selectable = true;
@ -1001,7 +1046,7 @@ export const TradeOffers: React.FC<any> = ({
</Button>
</DialogContent>
<DialogActions>
<Button variant="contained" onClick={onOkInfo} autoFocus>
<Button variant="outlined" onClick={onOkInfo} autoFocus>
Close
</Button>
</DialogActions>
@ -1012,9 +1057,19 @@ export const TradeOffers: React.FC<any> = ({
open={isShowTradesUnknownFee}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
PaperProps={{
style: {
backgroundColor: "rgb(39, 40, 44)",
background: "rgb(39, 40, 44)",
},
}}
>
<DialogTitle id="alert-dialog-title">Warning</DialogTitle>
<DialogContent>
<DialogTitle sx={{
background: "rgb(39, 40, 44)"
}} id="alert-dialog-title">Warning</DialogTitle>
<DialogContent sx={{ borderColor: "#333" }}>
<DialogContentText
id="alert-dialog-description"
sx={{ color: "white" }}
@ -1076,16 +1131,18 @@ export const TradeOffers: React.FC<any> = ({
</DialogContent>
<DialogActions>
<DialogActions sx={{
background: "rgb(39, 40, 44)",
}}>
<Button
variant="contained"
variant="outlined"
onClick={onCancelTradesUnknownFee}
autoFocus
>
Close
</Button>
<Button
variant="contained"
variant="outlined"
onClick={onOkTradesUnknownFee}
autoFocus
>
@ -1094,6 +1151,61 @@ export const TradeOffers: React.FC<any> = ({
</DialogActions>
</Dialog>
)}
{isShowAskToUpdateFee && (
<Dialog
open={isShowAskToUpdateFee}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
PaperProps={{
style: {
backgroundColor: "rgb(39, 40, 44)",
background: "rgb(39, 40, 44)",
},
}}
>
<DialogTitle sx={{
background: "rgb(39, 40, 44)",
}} id="alert-dialog-title">Suggestion</DialogTitle>
<DialogContent sx={{ borderColor: "#333" }}>
<DialogContentText
id="alert-dialog-description"
sx={{ color: "white" }}
>
Your current unlocking fee is higher than necessary. You can lower it to match the highest required fee and reduce costs.
</DialogContentText>
<Spacer height="20px" />
</DialogContent>
<DialogActions sx={{
background: "rgb(39, 40, 44)",
}}>
<Button
variant="outlined"
onClick={onOkAskToUpdateFee}
>
Continue without updating
</Button>
<Button
variant="outlined"
onClick={async (e)=> {
try {
await updateFee(messageAskToUpdateFee.message)
} catch (error) {
console.error(error)
}
onOkAskToUpdateFee(e)
}}
>
Lower fee
</Button>
</DialogActions>
</Dialog>
)}
{isShowBuyInProgress && (
<Dialog
open={isShowBuyInProgress}

View File

@ -30,7 +30,11 @@ import { usePublish, Service, QortalGetMetadata } from "qapp-core";
import { SetLeftFeature } from "ag-grid-community";
import { formatTimestampForum } from "../../utils/formatTime";
import { useAtom } from "jotai/react";
import { isEnabledCustomLockingFeeAtom, selectedFeePublisherAtom } from "../../global/state";
import {
isEnabledCustomLockingFeeAtom,
selectedFeePublisherAtom,
} from "../../global/state";
import { useRecommendedFees } from "../../hooks/useRecommendedFees";
type FeeEstimate = {
height: number;
@ -39,15 +43,15 @@ type FeeEstimate = {
medium_fee_per_kb: number;
high_fee_per_kb: number;
};
function isValidFeeEstimate(obj: any): obj is FeeEstimate {
export function isValidFeeEstimate(obj: any): obj is FeeEstimate {
return (
typeof obj === 'object' &&
typeof obj === "object" &&
obj !== null &&
typeof obj.height === 'number' &&
typeof obj.time === 'number' &&
typeof obj.low_fee_per_kb === 'number' &&
typeof obj.medium_fee_per_kb === 'number' &&
typeof obj.high_fee_per_kb === 'number'
typeof obj.height === "number" &&
typeof obj.time === "number" &&
typeof obj.low_fee_per_kb === "number" &&
typeof obj.medium_fee_per_kb === "number" &&
typeof obj.high_fee_per_kb === "number"
);
}
@ -56,9 +60,9 @@ function calculateFeeFromRate(feePerKb, sizeInBytes) {
return fee?.toFixed(0);
}
function calculateRateFromFee(totalFee, sizeInBytes) {
export function calculateRateFromFee(totalFee, sizeInBytes) {
const fee = (totalFee / sizeInBytes) * 1000;
return fee.toFixed(0)
return fee.toFixed(0);
}
export const FeeManager = ({ selectedCoin, setFee, fee }) => {
@ -68,12 +72,19 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
service: "JSON",
});
const { resource } = usePublish(3, "JSON", feeLocation);
const [selectedFeePublisher, setSelectedFeePublisher] = useAtom(selectedFeePublisherAtom)
const [isEnabledCustomLockingFee, setIsEnabledCustomLockingFee] = useAtom(isEnabledCustomLockingFeeAtom)
const [selectedFeePublisher, setSelectedFeePublisher] = useAtom(
selectedFeePublisherAtom
);
const [isEnabledCustomLockingFee, setIsEnabledCustomLockingFee] = useAtom(
isEnabledCustomLockingFeeAtom
);
const [editFee, setEditFee] = useState("");
const [openModal, setOpenModal] = useState(false);
const [recommendedFee, setRecommendedFee] = useState("m");
const [recommendedFee, setRecommendedFee] = useState("medium_fee_per_kb");
const {hideRecommendations, recommendedFeeDisplay} = useRecommendedFees({selectedCoin, recommendedFee})
const [openAlert, setOpenAlert] = useState(false);
const [info, setInfo] = useState<any>(null);
const { getCoinLabel } = useContext(gameContext);
@ -89,16 +100,18 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
setInfo(null);
};
const coin = useMemo(() => {
const coinLabel = getCoinLabel(selectedCoin)
if(typeof coinLabel !== 'string') return null
const coinLabel = getCoinLabel(selectedCoin);
if (typeof coinLabel !== "string") return null;
return coinLabel?.toLowerCase();
}, [selectedCoin, getCoinLabel]);
const feeTimestamp = useMemo(()=> {
if(!resource?.qortalMetadata?.identifier?.includes(`${coin.toUpperCase()}`)) return
return resource?.data?.time || null
}, [resource, coin])
const feeTimestamp = useMemo(() => {
if (
!resource?.qortalMetadata?.identifier?.includes(`${coin.toUpperCase()}`)
)
return;
return resource?.data?.time || null;
}, [resource, coin]);
const establishUpdateFeeForm = useCallback(async (coin) => {
setFee("");
// if the coin or type is not set, then abort
@ -117,10 +130,9 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
},
1800000
);
if ((response !== null && response !== undefined) && !isNaN(+response)) {
if (response !== null && response !== undefined && !isNaN(+response)) {
setFee(response);
}
} catch (error) {
setFee("");
console.error(error);
@ -131,42 +143,29 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
establishUpdateFeeForm(coin);
}, [coin, establishUpdateFeeForm]);
const recommendedFeeData = useMemo(() => {
if(!resource?.qortalMetadata?.identifier?.includes(`${coin.toUpperCase()}`)) return
if (!resource?.data) return null;
const isValid = isValidFeeEstimate(resource.data)
if(!isValid) return null
return resource.data;
}, [resource, coin]);
const recommendedFeeDisplay = useMemo(() => {
if (!recommendedFeeData) return null;
if(!recommendedFeeData) return null
return recommendedFeeData[recommendedFee] || null;
}, [recommendedFeeData, recommendedFee]);
const hideRecommendations = useMemo(()=> {
if(recommendedFeeData) return false
return true
}, [recommendedFeeData])
useEffect(()=> {
if(hideRecommendations){
setRecommendedFee('custom')
useEffect(() => {
if (hideRecommendations) {
setRecommendedFee("custom");
}
}, [hideRecommendations])
}, [hideRecommendations]);
const updateFee = async () => {
const typeRequest = "feerequired";
const typeRequestLocking = "feekb";
try {
let feeToSave = editFee
if(recommendedFee !== 'custom'){
feeToSave = calculateFeeFromRate(recommendedFeeDisplay, 300)
}
let feeToSave = editFee;
if (recommendedFee !== "custom") {
feeToSave = calculateFeeFromRate(recommendedFeeDisplay, 300);
}
if(+fee === +feeToSave){
return
}
const response = await qortalRequestWithTimeout(
{
action: "UPDATE_FOREIGN_FEE",
@ -177,8 +176,8 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
1800000
);
if(!isEnabledCustomLockingFee){
await qortalRequestWithTimeout(
if (!isEnabledCustomLockingFee) {
await qortalRequestWithTimeout(
{
action: "UPDATE_FOREIGN_FEE",
coin: coin,
@ -187,7 +186,6 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
},
1800000
);
}
if (response && !isNaN(+response)) {
@ -212,34 +210,11 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
event: React.MouseEvent<HTMLElement>,
newAlignment: string
) => {
if(newAlignment){
setRecommendedFee(newAlignment);
if (newAlignment) {
setRecommendedFee(newAlignment);
}
};
const getLatestFees = useCallback(async () => {
try {
const coinLabel = getCoinLabel(selectedCoin)
if(typeof coinLabel !== 'string') return
const coin = coinLabel?.toUpperCase();
const identifier = `coinInfo-${coin}`
const res = await fetch(
`/arbitrary/resources/searchsimple?service=JSON&identifier=${identifier}&name=${selectedFeePublisher}&prefix=true&limit=1&reverse=true`
);
const data = await res.json();
if (data && data?.length > 0) {
setFeeLocation(data[0]);
}
} catch (error) {
console.error(error)
}
}, [selectedFeePublisher, selectedCoin]);
useEffect(() => {
getLatestFees();
}, [getLatestFees]);
if (fee === null || fee === undefined) return;
@ -281,12 +256,12 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
open={openModal}
backdrop
styles={{
width: '450px',
maxWidth: '95vw',
padding: '15px'
width: "450px",
maxWidth: "95vw",
padding: "15px",
}}
>
<CoinActionContainer >
<CoinActionContainer>
<CoinActionRow>
<HeaderRow>
<Typography
@ -301,59 +276,83 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
</CoinActionRow>
<CoinActionRow>
<HeaderRow>
<Box sx={{
width: '100%'
}}>
<Box sx={{
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}}>
<CustomLabel sx={{
fontSize: '16px'
}} htmlFor="standard-adornment-name">
Recommended fee selection (in sats)
</CustomLabel>
<Spacer height="10px" />
<ToggleButtonGroup
color="primary"
value={recommendedFee}
exclusive
onChange={handleChange}
aria-label="Platform"
<Box
sx={{
width: "100%",
}}
>
<Box
sx={{
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
{!hideRecommendations && (
<CustomLabel
sx={{
fontSize: "16px",
}}
htmlFor="standard-adornment-name"
>
Recommended fee selection (in sats)
</CustomLabel>
<Spacer height="10px" />
<ToggleButtonGroup
color="primary"
value={recommendedFee}
exclusive
onChange={handleChange}
aria-label="Platform"
>
{!hideRecommendations && (
<>
<ToggleButton value="low_fee_per_kb">Low</ToggleButton>
<ToggleButton value="medium_fee_per_kb">Medium</ToggleButton>
<ToggleButton value="high_fee_per_kb">High</ToggleButton>
<ToggleButton value="low_fee_per_kb">
Low
</ToggleButton>
<ToggleButton value="medium_fee_per_kb">
Medium
</ToggleButton>
<ToggleButton value="high_fee_per_kb">
High
</ToggleButton>
</>
)}
<ToggleButton value="custom">Custom</ToggleButton>
</ToggleButtonGroup>
)}
<ToggleButton value="custom">Custom</ToggleButton>
</ToggleButtonGroup>
</Box>
{recommendedFeeDisplay && (
<>
<Spacer height="15px" />
<Box sx={{
width: '100%',
display: 'flex',
justifyContent: 'center'
}}>
<Typography
<Box
sx={{
color: "white",
fontSize: "18px",
width: "100%",
display: "flex",
justifyContent: "center",
}}
>
<span style={{
fontWeight: 'bold'
}}> New fee:</span>{" "}
{calculateFeeFromRate(recommendedFeeDisplay, 300)} sats
</Typography>
<Typography
sx={{
color: "white",
fontSize: "18px",
}}
>
<span
style={{
fontWeight: "bold",
}}
>
{" "}
New fee:
</span>{" "}
{calculateFeeFromRate(
recommendedFeeDisplay,
300
)}{" "}
sats
</Typography>
</Box>
<Spacer height="10px" />
<Box
@ -376,11 +375,10 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
>
This recommended fee is derived from{" "}
{recommendedFeeDisplay} per kb, for a transaction that
is approximately 300 kB in size.
is approximately 300 bytes in size.
</Typography>
</Box>
<Spacer height="10px"/>
<Spacer height="10px" />
</>
)}
</Box>
@ -408,7 +406,7 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
<ButtonBase
onClick={updateFee}
disabled={(recommendedFee === 'custom' && !editFee)}
disabled={recommendedFee === "custom" && !editFee}
sx={{
minHeight: "42px",
border: "1px solid gray",
@ -431,10 +429,15 @@ export const FeeManager = ({ selectedCoin, setFee, fee }) => {
<Typography>Update fee</Typography>
</ButtonBase>
{!hideRecommendations && feeTimestamp && (
<CustomLabel sx={{
textAlign: 'center'
}}>*Recommended fees last updated: {formatTimestampForum(feeTimestamp)}</CustomLabel>
)}
<CustomLabel
sx={{
textAlign: "center",
}}
>
*Recommended fees last updated:{" "}
{formatTimestampForum(feeTimestamp)}
</CustomLabel>
)}
</CoinActionContainer>
</ReusableModal>
)}

View File

@ -39,20 +39,34 @@ import { SetLeftFeature } from "ag-grid-community";
import { formatTimestampForum } from "../../utils/formatTime";
import { SelectRow } from "../header/Header";
import { useAtom } from "jotai/react";
import { isEnabledCustomLockingFeeAtom, selectedFeePublisherAtom } from "../../global/state";
import {
isEnabledCustomLockingFeeAtom,
selectedFeePublisherAtom,
} from "../../global/state";
import { useRecommendedFees } from "../../hooks/useRecommendedFees";
export const Settings = () => {
const saveDataLocal = useGlobal().persistentOperations.saveData
const getDataLocal = useGlobal().persistentOperations.getData
const saveDataLocal = useGlobal().persistentOperations.saveData;
const getDataLocal = useGlobal().persistentOperations.getData;
const [openModal, setOpenModal] = useState(false);
const [lockingFee, setLockingFee] = useState("");
const [recommendedFee, setRecommendedFee] = useState("medium_fee_per_kb");
const [selectedCoin, setSelectedCoin] = useState("LITECOIN");
const { hideRecommendations, recommendedFeeDisplay, coin } = useRecommendedFees({
selectedCoin,
recommendedFee
});
const [editLockingFee, setEditLockingFee] = useState("");
const [openAlert, setOpenAlert] = useState(false);
const [info, setInfo] = useState<any>(null);
const [selectedCoin, setSelectedCoin] = useState("LTC");
const [selectedFeePublisher, setSelectedFeePublisher] = useAtom(selectedFeePublisherAtom)
const [isEnabledCustomLockingFee, setIsEnabledCustomLockingFee] = useAtom(isEnabledCustomLockingFeeAtom)
const [selectedFeePublisher, setSelectedFeePublisher] = useAtom(
selectedFeePublisherAtom
);
const [isEnabledCustomLockingFee, setIsEnabledCustomLockingFee] = useAtom(
isEnabledCustomLockingFeeAtom
);
const handleCloseAlert = (
event?: React.SyntheticEvent | Event,
reason?: SnackbarCloseReason
@ -69,12 +83,14 @@ export const Settings = () => {
const typeRequest = "feekb";
try {
const feeToSave = editLockingFee;
let feeToSave = editLockingFee;
if (recommendedFee !== "custom") {
feeToSave = recommendedFeeDisplay
}
const response = await qortalRequestWithTimeout(
{
action: "UPDATE_FOREIGN_FEE",
coin: selectedCoin,
coin: coin,
type: typeRequest,
value: feeToSave,
},
@ -102,7 +118,16 @@ export const Settings = () => {
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setIsEnabledCustomLockingFee(event.target.checked);
saveDataLocal('isEnabledCustomLockingFee', event.target.checked)
saveDataLocal("isEnabledCustomLockingFee", event.target.checked);
};
const handleChangeRecommended = (
event: React.MouseEvent<HTMLElement>,
newAlignment: string
) => {
if (newAlignment) {
setRecommendedFee(newAlignment);
}
};
const establishUpdateFeeForm = useCallback(async (coin) => {
@ -136,27 +161,33 @@ export const Settings = () => {
}, []);
useEffect(() => {
if(!openModal) return
establishUpdateFeeForm(selectedCoin);
}, [selectedCoin, establishUpdateFeeForm, openModal]);
if (!openModal) return;
establishUpdateFeeForm(coin);
}, [coin, establishUpdateFeeForm, openModal]);
useEffect(()=> {
const getSavedSelectedPublisher = async ()=> {
useEffect(() => {
const getSavedSelectedPublisher = async () => {
try {
const res = await getDataLocal('selectedFeePublisher')
if(res){
setSelectedFeePublisher(res)
const res = await getDataLocal("selectedFeePublisher");
if (res) {
setSelectedFeePublisher(res);
}
const res2 = await getDataLocal('isEnabledCustomLockingFee')
if(res2){
setIsEnabledCustomLockingFee(res)
const res2 = await getDataLocal("isEnabledCustomLockingFee");
if (res2) {
setIsEnabledCustomLockingFee(res);
}
} catch (error) {
console.error(error)
console.error(error);
}
}
getSavedSelectedPublisher()
}, [])
};
getSavedSelectedPublisher();
}, []);
useEffect(() => {
if (hideRecommendations) {
setRecommendedFee("custom");
}
}, [hideRecommendations]);
return (
<>
@ -187,18 +218,33 @@ export const Settings = () => {
}}
open={openModal}
>
<CoinActionContainer sx={{
border: '1px solid #3F3F3F',
borderRadius: '5px',
padding: '5px'
}}>
<CoinActionContainer
sx={{
border: "1px solid #3F3F3F",
borderRadius: "5px",
padding: "5px",
}}
>
<Typography>Locking fees</Typography>
<FormControlLabel control={<Checkbox checked={isEnabledCustomLockingFee} onChange={handleChange} />} label="Enable custom locking fee" />
<FormControlLabel
control={
<Checkbox
checked={isEnabledCustomLockingFee}
onChange={handleChange}
/>
}
label="Enable custom locking fee"
/>
{isEnabledCustomLockingFee && (
<CoinSelectRow sx={{
gap: '20px'
}}>
<>
<CoinSelectRow
sx={{
gap: "20px",
width: '100%',
justifyContent: 'center'
}}
>
<Select
size="small"
value={selectedCoin}
@ -208,25 +254,119 @@ export const Settings = () => {
setSelectedCoin(e.target.value);
}}
>
<MenuItem value={"LTC"}>
<MenuItem value={"LITECOIN"}>
<SelectRow coin="LTC" />
</MenuItem>
<MenuItem value={"DOGE"}>
<MenuItem value={"DOGECOIN"}>
<SelectRow coin="DOGE" />
</MenuItem>
<MenuItem value={"BTC"}>
<MenuItem value={"BITCOIN"}>
<SelectRow coin="BTC" />
</MenuItem>
<MenuItem value={"DGB"}>
<MenuItem value={"DIGIBYTE"}>
<SelectRow coin="DGB" />
</MenuItem>
<MenuItem value={"RVN"}>
<MenuItem value={"RAVENCOIN"}>
<SelectRow coin="RVN" />
</MenuItem>
<MenuItem value={"PIRATECHAIN"}>
<SelectRow coin="ARRR" />
</MenuItem>
</Select>
<Box>
</Box>
</CoinSelectRow>
<CoinActionRow>
<HeaderRow>
<Box
sx={{
width: "100%",
}}
>
<Box
sx={{
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<CustomLabel
sx={{
fontSize: "16px",
}}
htmlFor="standard-adornment-name"
>
Recommended fee selection (in sats per kb)
</CustomLabel>
<Spacer height="10px" />
<ToggleButtonGroup
color="primary"
value={recommendedFee}
exclusive
onChange={handleChangeRecommended}
aria-label="Platform"
>
{!hideRecommendations && (
<>
<ToggleButton value="low_fee_per_kb">
Low
</ToggleButton>
<ToggleButton value="medium_fee_per_kb">
Medium
</ToggleButton>
<ToggleButton value="high_fee_per_kb">
High
</ToggleButton>
</>
)}
<ToggleButton value="custom">Custom</ToggleButton>
</ToggleButtonGroup>
</Box>
{recommendedFeeDisplay && (
<>
<Spacer height="15px" />
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "center",
}}
>
<Typography
sx={{
color: "white",
fontSize: "18px",
}}
>
<span
style={{
fontWeight: "bold",
}}
>
{" "}
New fee:
</span>{" "}
{recommendedFeeDisplay}{" "}
sats per kb
</Typography>
</Box>
<Spacer height="10px" />
</>
)}
</Box>
</HeaderRow>
</CoinActionRow>
{recommendedFee === "custom" && (
<CoinActionRow>
<HeaderRow>
<Box>
<CustomLabel htmlFor="standard-adornment-name">
Locking fee for {selectedCoin} (sats per kb)
Custom fee
</CustomLabel>
<Spacer height="5px" />
<CustomInput
@ -237,13 +377,16 @@ export const Settings = () => {
autoComplete="off"
/>
</Box>
</CoinSelectRow>
</HeaderRow>
</CoinActionRow>
)}
</>
)}
<ButtonBase
onClick={updateLockingFee}
disabled={!editLockingFee}
disabled={recommendedFee === "custom" && !editLockingFee}
sx={{
minHeight: "42px",
border: "1px solid gray",
@ -266,32 +409,33 @@ export const Settings = () => {
<Typography>Update locking fee</Typography>
</ButtonBase>
</CoinActionContainer>
<Spacer height="20px"/>
<CoinActionContainer sx={{
border: '1px solid #3F3F3F',
borderRadius: '5px',
padding: '5px'
}}>
<Spacer height="20px" />
<CoinActionContainer
sx={{
border: "1px solid #3F3F3F",
borderRadius: "5px",
padding: "5px",
}}
>
<Typography>Fee publisher</Typography>
<Select
size="small"
value={selectedFeePublisher}
onChange={(e) => {
if(e.target.value){
setSelectedFeePublisher(e.target.value);
saveDataLocal('selectedFeePublisher', e.target.value)
}
}}
>
<MenuItem value={"Foreign-Fee-Publisher"}>
<SelectRow coin="Foreign-Fee-Publisher" />
</MenuItem>
<MenuItem value={"Ice.JSON"}>
<SelectRow coin="Ice.JSON" />
</MenuItem>
</Select>
</CoinActionContainer>
size="small"
value={selectedFeePublisher}
onChange={(e) => {
if (e.target.value) {
setSelectedFeePublisher(e.target.value);
saveDataLocal("selectedFeePublisher", e.target.value);
}
}}
>
<MenuItem value={"Foreign-Fee-Publisher"}>
<SelectRow coin="Foreign-Fee-Publisher" />
</MenuItem>
<MenuItem value={"Ice.JSON"}>
<SelectRow coin="Ice.JSON" />
</MenuItem>
</Select>
</CoinActionContainer>
</ReusableModal>
)}
<Snackbar

View File

@ -0,0 +1,76 @@
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'
import gameContext from '../contexts/gameContext';
import { QortalGetMetadata, usePublish } from 'qapp-core';
import { useAtom } from 'jotai/react';
import { selectedFeePublisherAtom } from '../global/state';
import { isValidFeeEstimate } from '../components/sell/FeeManager';
export const useRecommendedFees = ({selectedCoin, recommendedFee}) => {
const { getCoinLabel } = useContext(gameContext);
const [selectedFeePublisher, setSelectedFeePublisher] = useAtom(
selectedFeePublisherAtom
);
const [feeLocation, setFeeLocation] = useState<QortalGetMetadata>({
name: "",
identifier: "",
service: "JSON",
});
const { resource } = usePublish(3, "JSON", feeLocation);
const coin = useMemo(() => {
const coinLabel = getCoinLabel(selectedCoin);
if (typeof coinLabel !== "string") return null;
return coinLabel?.toLowerCase();
}, [selectedCoin, getCoinLabel]);
const getLatestFees = useCallback(async () => {
try {
const coinLabel = getCoinLabel(selectedCoin);
if (typeof coinLabel !== "string") return;
const coin = coinLabel?.toUpperCase();
const identifier = `coinInfo-${coin}`;
const res = await fetch(
`/arbitrary/resources/searchsimple?service=JSON&identifier=${identifier}&name=${selectedFeePublisher}&prefix=true&limit=1&reverse=true`
);
const data = await res.json();
if (data && data?.length > 0) {
setFeeLocation(data[0]);
}
} catch (error) {
console.error(error);
}
}, [selectedFeePublisher, selectedCoin]);
useEffect(() => {
getLatestFees();
}, [getLatestFees]);
const recommendedFeeData = useMemo(() => {
if (
!resource?.qortalMetadata?.identifier?.includes(`${coin.toUpperCase()}`)
)
return;
if (!resource?.data) return null;
const isValid = isValidFeeEstimate(resource.data);
if (!isValid) return null;
return resource.data;
}, [resource, coin]);
const recommendedFeeDisplay = useMemo(() => {
if (!recommendedFeeData) return null;
if (!recommendedFeeData) return null;
return recommendedFeeData[recommendedFee] || null;
}, [recommendedFeeData, recommendedFee]);
const hideRecommendations = useMemo(() => {
if (recommendedFeeData) return false;
return true;
}, [recommendedFeeData]);
return {
hideRecommendations,
recommendedFeeDisplay,
coin
}
}

View File

@ -0,0 +1,61 @@
import { useAtom } from 'jotai/react';
import React, { useCallback, useContext, useMemo } from 'react'
import { calculateRateFromFee } from '../components/sell/FeeManager';
import { isEnabledCustomLockingFeeAtom } from '../global/state';
import gameContext from '../contexts/gameContext';
export const useUpdateFee = ({setFee, selectedCoin}) => {
const [isEnabledCustomLockingFee, setIsEnabledCustomLockingFee] = useAtom(
isEnabledCustomLockingFeeAtom
);
const {
getCoinLabel,
} = useContext(gameContext);
const coin = useMemo(() => {
const coinLabel = getCoinLabel(selectedCoin);
if (typeof coinLabel !== "string") return null;
return coinLabel?.toLowerCase();
}, [selectedCoin, getCoinLabel]);
const updateFee = useCallback(async (suggestedFee ) => {
const typeRequest = "feerequired";
const typeRequestLocking = "feekb";
const feeToSave = +suggestedFee
const response = await qortalRequestWithTimeout(
{
action: "UPDATE_FOREIGN_FEE",
coin: coin,
type: typeRequest,
value: feeToSave,
},
1800000
);
if (!isEnabledCustomLockingFee) {
await qortalRequestWithTimeout(
{
action: "UPDATE_FOREIGN_FEE",
coin: coin,
type: typeRequestLocking,
value: calculateRateFromFee(feeToSave, 300),
},
1800000
);
}
if (response && !isNaN(+response)) {
setFee(response);
return response
} else throw new Error("Unable to update fee");
}, [coin, isEnabledCustomLockingFee, setFee]);
return updateFee
}

View File

@ -113,8 +113,8 @@ export const HomePage = () => {
</TextTableTitle>
</Box>
<Spacer height="10px" />
<TradeOffers fee={fee}
setFee={setFee} foreignCoinBalance={foreignCoinBalance} />
<TradeOffers setFee={setFee} fee={fee}
foreignCoinBalance={foreignCoinBalance} />
</div>
<CreateSell show={mode === "sell"} qortAddress={userInfo?.address} />