mirror of
https://github.com/Qortal/q-trade.git
synced 2025-06-18 12:11:21 +00:00
manage fees
This commit is contained in:
parent
4bb3ca2685
commit
c609fbb054
576
package-lock.json
generated
576
package-lock.json
generated
@ -18,10 +18,10 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"react": "^18.2.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"qapp-core": "^1.0.22",
|
||||
"react": "^19.1.0",
|
||||
"react-countdown-circle-timer": "^3.2.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-ga4": "^2.1.0",
|
||||
"react-loader-spinner": "^6.1.6",
|
||||
"react-qr-code": "^2.0.15",
|
||||
@ -33,8 +33,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.17.5",
|
||||
"@types/react": "^18.2.66",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||
"@typescript-eslint/parser": "^7.2.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
@ -1791,15 +1791,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin": {
|
||||
"version": "11.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
|
||||
"integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
|
||||
"version": "11.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
|
||||
"integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.16.7",
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/hash": "^0.9.1",
|
||||
"@emotion/memoize": "^0.8.1",
|
||||
"@emotion/serialize": "^1.1.2",
|
||||
"@emotion/hash": "^0.9.2",
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"convert-source-map": "^1.5.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
@ -1808,6 +1808,11 @@
|
||||
"stylis": "4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
|
||||
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||
@ -1841,11 +1846,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
|
||||
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
|
||||
},
|
||||
"node_modules/@emotion/cache/node_modules/@emotion/weak-memoize": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
|
||||
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
|
||||
},
|
||||
"node_modules/@emotion/hash": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
|
||||
@ -1865,17 +1865,17 @@
|
||||
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
|
||||
},
|
||||
"node_modules/@emotion/react": {
|
||||
"version": "11.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz",
|
||||
"integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==",
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
|
||||
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.11.0",
|
||||
"@emotion/cache": "^11.11.0",
|
||||
"@emotion/serialize": "^1.1.3",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
|
||||
"@emotion/utils": "^1.2.1",
|
||||
"@emotion/weak-memoize": "^0.3.1",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/cache": "^11.14.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"@emotion/weak-memoize": "^0.4.0",
|
||||
"hoist-non-react-statics": "^3.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@ -1915,16 +1915,16 @@
|
||||
"integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="
|
||||
},
|
||||
"node_modules/@emotion/styled": {
|
||||
"version": "11.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz",
|
||||
"integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==",
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz",
|
||||
"integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.11.0",
|
||||
"@emotion/is-prop-valid": "^1.2.2",
|
||||
"@emotion/serialize": "^1.1.4",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
|
||||
"@emotion/utils": "^1.2.1"
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/is-prop-valid": "^1.3.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
|
||||
"@emotion/utils": "^1.4.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.0.0-rc.0",
|
||||
@ -1936,15 +1936,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/styled/node_modules/@emotion/is-prop-valid": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz",
|
||||
"integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==",
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "^0.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/styled/node_modules/@emotion/memoize": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
|
||||
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
|
||||
},
|
||||
"node_modules/@emotion/unitless": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
|
||||
"integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
|
||||
"integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/use-insertion-effect-with-fallbacks": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
|
||||
"integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
|
||||
"integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
@ -1955,9 +1969,9 @@
|
||||
"integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="
|
||||
},
|
||||
"node_modules/@emotion/weak-memoize": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
|
||||
"integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
|
||||
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.20.2",
|
||||
@ -3109,6 +3123,31 @@
|
||||
"string.prototype.matchall": "^4.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-virtual": {
|
||||
"version": "3.13.6",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.6.tgz",
|
||||
"integrity": "sha512-WT7nWs8ximoQ0CDx/ngoFP7HbQF9Q2wQe4nh2NB+u2486eX3nZRE40P9g6ccCVq7ZfTSH5gFOuCoVH5DLNS/aA==",
|
||||
"dependencies": {
|
||||
"@tanstack/virtual-core": "3.13.6"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/virtual-core": {
|
||||
"version": "3.13.6",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.6.tgz",
|
||||
"integrity": "sha512-cnQUeWnhNP8tJ4WsGcYiX24Gjkc9ALstLbHcBj1t3E7EimN6n6kHH+DPV4PpDnuw00NApQp+ViojMj1GRdwYQg==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
@ -3179,21 +3218,22 @@
|
||||
"integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.2.79",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz",
|
||||
"integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==",
|
||||
"version": "19.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz",
|
||||
"integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-dom": {
|
||||
"version": "18.2.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.25.tgz",
|
||||
"integrity": "sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==",
|
||||
"version": "19.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.3.tgz",
|
||||
"integrity": "sha512-rJXC08OG0h3W6wDMFxQrZF00Kq6qQvw0djHRdzl3U5DnIERz0MRce3WVc7IS6JYBwtaP/DwYtRRjVlvivNveKg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-transition-group": {
|
||||
@ -3210,6 +3250,11 @@
|
||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
||||
"dev": true
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
|
||||
@ -3219,13 +3264,14 @@
|
||||
"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=="
|
||||
"integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "7.7.0",
|
||||
@ -3610,6 +3656,14 @@
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/attr-accept": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz",
|
||||
"integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/available-typed-arrays": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||
@ -3703,6 +3757,33 @@
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
@ -3714,6 +3795,29 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/bloom-filters": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/bloom-filters/-/bloom-filters-3.0.4.tgz",
|
||||
"integrity": "sha512-BdnPWo2OpYhlvuP2fRzJBdioMCkm7Zp0HCf8NJgF5Mbyqy7VQ/CnTiVWMMyq4EZCBHwj0Kq6098gW2/3RsZsrA==",
|
||||
"dependencies": {
|
||||
"@types/seedrandom": "^3.0.8",
|
||||
"base64-arraybuffer": "^1.0.2",
|
||||
"is-buffer": "^2.0.5",
|
||||
"lodash": "^4.17.21",
|
||||
"long": "^5.2.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"seedrandom": "^3.0.5",
|
||||
"xxhashjs": "^0.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
@ -3766,6 +3870,29 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
@ -3815,6 +3942,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
|
||||
"integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@ -3933,6 +4061,15 @@
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/compressorjs": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/compressorjs/-/compressorjs-1.2.1.tgz",
|
||||
"integrity": "sha512-+geIjeRnPhQ+LLvvA7wxBQE5ddeLU7pJ3FsKFWirDw6veY3s9iLxAQEw7lXGHnhCJvBujEQWuNnGzZcvCvdkLQ==",
|
||||
"dependencies": {
|
||||
"blueimp-canvas-to-blob": "^3.29.0",
|
||||
"is-blob": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@ -3945,14 +4082,6 @@
|
||||
"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",
|
||||
@ -3995,6 +4124,11 @@
|
||||
"node": ">= 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=="
|
||||
},
|
||||
"node_modules/crypto-random-string": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
|
||||
@ -4008,6 +4142,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
||||
"integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
@ -4016,6 +4151,7 @@
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"camelize": "^1.0.0",
|
||||
"css-color-keywords": "^1.0.0",
|
||||
@ -4027,6 +4163,11 @@
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/cuint": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz",
|
||||
"integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw=="
|
||||
},
|
||||
"node_modules/data-view-buffer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
|
||||
@ -4078,6 +4219,11 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
@ -4179,11 +4325,20 @@
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz",
|
||||
"integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/ejs": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||
@ -4778,6 +4933,22 @@
|
||||
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
|
||||
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
|
||||
},
|
||||
"node_modules/file-selector": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz",
|
||||
"integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/filelist": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||
@ -5110,6 +5281,14 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/goober": {
|
||||
"version": "2.1.16",
|
||||
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz",
|
||||
"integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==",
|
||||
"peerDependencies": {
|
||||
"csstype": "^3.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
@ -5232,6 +5411,25 @@
|
||||
"integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
|
||||
@ -5344,6 +5542,17 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-blob": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.1.0.tgz",
|
||||
"integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-boolean-object": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
|
||||
@ -5360,6 +5569,28 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-buffer": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
|
||||
"integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/is-builtin-module": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
|
||||
@ -5901,6 +6132,11 @@
|
||||
"integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
|
||||
"dev": true
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
@ -6208,9 +6444,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
||||
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw=="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
@ -6233,9 +6470,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.38",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
|
||||
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
|
||||
"version": "8.4.49",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
|
||||
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -6250,10 +6487,11 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.2.0"
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
@ -6262,7 +6500,8 @@
|
||||
"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=="
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
@ -6314,6 +6553,33 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qapp-core": {
|
||||
"version": "1.0.22",
|
||||
"resolved": "https://registry.npmjs.org/qapp-core/-/qapp-core-1.0.22.tgz",
|
||||
"integrity": "sha512-3q8Ebr9lpyDW7lTo91Rlak2EGIAXrU9DYhUBuLsYZMuRyI/bKoc0E4hHrrAo946eJ/5AxqNGmZDkYSJq5tOTZg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/react-virtual": "^3.13.2",
|
||||
"bloom-filters": "^3.0.4",
|
||||
"buffer": "^6.0.3",
|
||||
"compressorjs": "^1.2.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"dompurify": "^3.2.4",
|
||||
"react-dropzone": "^14.3.8",
|
||||
"react-hot-toast": "^2.5.2",
|
||||
"react-intersection-observer": "^9.16.0",
|
||||
"short-unique-id": "^5.2.0",
|
||||
"zustand": "^4.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@mui/icons-material": "^7.0.1",
|
||||
"@mui/material": "^7.0.1",
|
||||
"react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/qr.js": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz",
|
||||
@ -6349,28 +6615,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"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",
|
||||
@ -6380,15 +6632,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.0"
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0"
|
||||
"react": "^19.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dropzone": {
|
||||
"version": "14.3.8",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.3.8.tgz",
|
||||
"integrity": "sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==",
|
||||
"dependencies": {
|
||||
"attr-accept": "^2.2.4",
|
||||
"file-selector": "^2.1.0",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.8 || 18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-ga4": {
|
||||
@ -6396,6 +6664,36 @@
|
||||
"resolved": "https://registry.npmjs.org/react-ga4/-/react-ga4-2.1.0.tgz",
|
||||
"integrity": "sha512-ZKS7PGNFqqMd3PJ6+C2Jtz/o1iU9ggiy8Y8nUeksgVuvNISbmrQtJiZNvC/TjDsqD0QlU5Wkgs7i+w9+OjHhhQ=="
|
||||
},
|
||||
"node_modules/react-hot-toast": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz",
|
||||
"integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==",
|
||||
"dependencies": {
|
||||
"csstype": "^3.1.3",
|
||||
"goober": "^2.1.16"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16",
|
||||
"react-dom": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/react-intersection-observer": {
|
||||
"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==",
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
||||
@ -6484,6 +6782,7 @@
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
@ -6506,6 +6805,11 @@
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/regenerate": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||
@ -6782,12 +7086,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
"version": "0.26.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
|
||||
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.0",
|
||||
@ -6866,7 +7173,8 @@
|
||||
"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=="
|
||||
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
@ -6966,9 +7274,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@ -7122,16 +7431,17 @@
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "6.1.17",
|
||||
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.17.tgz",
|
||||
"integrity": "sha512-97D7DwWanI7nN24v0D4SvbfjLE9656umNSJZkBkDIWL37aZqG/wRQ+Y9pWtXyBIM/NSfcBzHLErEsqHmJNSVUg==",
|
||||
"license": "MIT",
|
||||
"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",
|
||||
"postcss": "8.4.49",
|
||||
"shallowequal": "1.1.0",
|
||||
"stylis": "4.3.2",
|
||||
"tslib": "2.6.2"
|
||||
@ -7151,7 +7461,8 @@
|
||||
"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=="
|
||||
"integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stylis": {
|
||||
"version": "4.2.0",
|
||||
@ -7301,11 +7612,6 @@
|
||||
"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",
|
||||
@ -7330,7 +7636,8 @@
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
@ -7567,6 +7874,14 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"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==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.2.9",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.9.tgz",
|
||||
@ -8087,6 +8402,14 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xxhashjs": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz",
|
||||
"integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==",
|
||||
"dependencies": {
|
||||
"cuint": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
@ -8112,6 +8435,33 @@
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "4.5.6",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz",
|
||||
"integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==",
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "^1.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=16.8",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
16
package.json
16
package.json
@ -20,10 +20,10 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"react": "^18.2.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"qapp-core": "^1.0.22",
|
||||
"react": "^19.1.0",
|
||||
"react-countdown-circle-timer": "^3.2.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-ga4": "^2.1.0",
|
||||
"react-loader-spinner": "^6.1.6",
|
||||
"react-qr-code": "^2.0.15",
|
||||
@ -35,8 +35,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.17.5",
|
||||
"@types/react": "^18.2.66",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||
"@typescript-eslint/parser": "^7.2.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
@ -46,5 +46,11 @@
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.0",
|
||||
"vite-plugin-pwa": "^0.20.5"
|
||||
},
|
||||
"overrides": {
|
||||
"react-loader-spinner": {
|
||||
"react": "^18 || ^19",
|
||||
"react-dom": "^18 || ^19"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@
|
||||
.ag-theme-alpine-dark .ag-row-odd,
|
||||
.ag-theme-alpine-dark .ag-row-even {
|
||||
width: 100%;
|
||||
background-color: #292929 !important; /* Replace with your desired color */
|
||||
background-color: #292929; /* Replace with your desired color */
|
||||
}
|
||||
|
||||
.ag-theme-alpine-dark {
|
||||
@ -143,4 +143,5 @@
|
||||
border-radius: 8px;
|
||||
background-clip: content-box;
|
||||
border: 4px solid transparent;
|
||||
}
|
||||
}
|
||||
|
||||
|
260
src/App.tsx
260
src/App.tsx
@ -1,7 +1,6 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import ReactGA from "react-ga4";
|
||||
import "./App.css";
|
||||
import socketService from "./services/socketService";
|
||||
import GameContext, {
|
||||
IContextProps,
|
||||
UserNameAvatar,
|
||||
@ -22,9 +21,7 @@ import axios from "axios";
|
||||
import { executeEvent } from "./utils/events";
|
||||
import { useIndexedDBContext } from "./contexts/indexedDBContext";
|
||||
import { useGetOngoingTransactions } from "./components/DbComponents/OngoingTransactions";
|
||||
|
||||
|
||||
|
||||
import { GlobalProvider } from "qapp-core";
|
||||
|
||||
export async function sendRequestToExtension(
|
||||
requestType: string,
|
||||
@ -66,26 +63,32 @@ export async function sendRequestToExtension(
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function App() {
|
||||
const [userInfo, setUserInfo] = useState<any>(null);
|
||||
const [qortBalance, setQortBalance] = useState<any>(null);
|
||||
const [balances, setBalances] = useState<any>({});
|
||||
const [selectedCoin, setSelectedCoin] = useState("LITECOIN");
|
||||
|
||||
const foreignCoinBalance = useMemo(()=> {
|
||||
if(balances[selectedCoin] === 0) return 0
|
||||
return balances[selectedCoin] || null
|
||||
}, [balances, selectedCoin])
|
||||
|
||||
const foreignCoinBalance = useMemo(() => {
|
||||
if (balances[selectedCoin] === 0) return 0;
|
||||
return balances[selectedCoin] || null;
|
||||
}, [balances, selectedCoin]);
|
||||
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
|
||||
const [OAuthLoading, setOAuthLoading] = useState<boolean>(false);
|
||||
const db = useIndexedDBContext();
|
||||
const [isUsingGateway, setIsUsingGateway] = useState(null)
|
||||
const [isUsingGateway, setIsUsingGateway] = useState(null);
|
||||
|
||||
const [isSocketUp, setIsSocketUp] = useState<boolean>(false);
|
||||
// const [onGoingTrades, setOngoingTrades] = useState([])
|
||||
const {onGoingTrades, fetchOngoingTransactions, updateTransactionInDB, deleteTemporarySellOrder, updateTemporaryFailedTradeBots, fetchTemporarySellOrders, sellOrders} = useGetOngoingTransactions({qortAddress: userInfo?.address})
|
||||
const {
|
||||
onGoingTrades,
|
||||
fetchOngoingTransactions,
|
||||
updateTransactionInDB,
|
||||
deleteTemporarySellOrder,
|
||||
updateTemporaryFailedTradeBots,
|
||||
fetchTemporarySellOrders,
|
||||
sellOrders,
|
||||
} = useGetOngoingTransactions({ qortAddress: userInfo?.address });
|
||||
const [userNameAvatar, setUserNameAvatar] = useState<
|
||||
Record<string, UserNameAvatar>
|
||||
>({});
|
||||
@ -101,27 +104,23 @@ function App() {
|
||||
setLoadingSlider,
|
||||
};
|
||||
|
||||
const getIsUsingGateway = async ()=> {
|
||||
const getIsUsingGateway = async () => {
|
||||
try {
|
||||
const res = await qortalRequest({
|
||||
action: "IS_USING_PUBLIC_NODE"
|
||||
})
|
||||
setIsUsingGateway(res)
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
action: "IS_USING_PUBLIC_NODE",
|
||||
});
|
||||
setIsUsingGateway(res);
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
|
||||
useEffect(()=> {
|
||||
getIsUsingGateway()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
getIsUsingGateway();
|
||||
}, []);
|
||||
|
||||
const resetNotification = () => {
|
||||
setNotification({ alertType: "", msg: "" });
|
||||
};
|
||||
|
||||
|
||||
const userContextValue: UserContextProps = {
|
||||
avatar,
|
||||
setAvatar,
|
||||
@ -164,53 +163,44 @@ function App() {
|
||||
askForAccountInformation();
|
||||
}, [askForAccountInformation]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const getQortBalance = async ()=> {
|
||||
const getQortBalance = async () => {
|
||||
const balanceUrl: string = `/addresses/balance/${userInfo?.address}`;
|
||||
const balanceResponse = await axios(balanceUrl);
|
||||
setQortBalance(balanceResponse.data?.value)
|
||||
}
|
||||
setQortBalance(balanceResponse.data?.value);
|
||||
};
|
||||
|
||||
const getLTCBalance = async (coin) => {
|
||||
try {
|
||||
const response = await qortalRequest({
|
||||
action: "GET_WALLET_BALANCE",
|
||||
coin: getCoinLabel(coin)
|
||||
coin: getCoinLabel(coin),
|
||||
});
|
||||
if(!response?.error){
|
||||
setBalances((prev)=> {
|
||||
if (!response?.error) {
|
||||
setBalances((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
[coin]: +response
|
||||
}
|
||||
})
|
||||
[coin]: +response,
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
//
|
||||
//
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if(!userInfo?.address || !selectedCoin) return
|
||||
if (!userInfo?.address || !selectedCoin) return;
|
||||
const intervalGetTradeInfo = setInterval(() => {
|
||||
fetchOngoingTransactions()
|
||||
getLTCBalance(selectedCoin)
|
||||
getQortBalance()
|
||||
}, 150000)
|
||||
getLTCBalance(selectedCoin)
|
||||
getQortBalance()
|
||||
fetchOngoingTransactions();
|
||||
getLTCBalance(selectedCoin);
|
||||
getQortBalance();
|
||||
}, 150000);
|
||||
getLTCBalance(selectedCoin);
|
||||
getQortBalance();
|
||||
return () => {
|
||||
clearInterval(intervalGetTradeInfo)
|
||||
}
|
||||
}, [userInfo?.address, isAuthenticated, selectedCoin])
|
||||
|
||||
clearInterval(intervalGetTradeInfo);
|
||||
};
|
||||
}, [userInfo?.address, isAuthenticated, selectedCoin]);
|
||||
|
||||
const handleMessage = async (event: any) => {
|
||||
if (event.data.type === "LOGOUT") {
|
||||
@ -218,31 +208,29 @@ function App() {
|
||||
setUserInfo(null);
|
||||
setAvatar("");
|
||||
setIsAuthenticated(false);
|
||||
setQortBalance(null)
|
||||
setQortBalance(null);
|
||||
localStorage.setItem("token", "");
|
||||
} else if(event.data.type === "RESPONSE_FOR_TRADES"){
|
||||
|
||||
|
||||
const response = event.data.payload
|
||||
if (response?.extra?.atAddresses
|
||||
) {
|
||||
} else if (event.data.type === "RESPONSE_FOR_TRADES") {
|
||||
const response = event.data.payload;
|
||||
if (response?.extra?.atAddresses) {
|
||||
try {
|
||||
const status = response.callResponse === true ? 'trade-ongoing' : 'trade-failed'
|
||||
const status =
|
||||
response.callResponse === true ? "trade-ongoing" : "trade-failed";
|
||||
const token = localStorage.getItem("token");
|
||||
// Prepare transaction data
|
||||
const transactionData = {
|
||||
qortalAtAddresses: response.extra.atAddresses,
|
||||
qortAddress: userInfo.address,
|
||||
status: status,
|
||||
message: response.extra.message,
|
||||
};
|
||||
// Prepare transaction data
|
||||
const transactionData = {
|
||||
qortalAtAddresses: response.extra.atAddresses,
|
||||
qortAddress: userInfo.address,
|
||||
status: status,
|
||||
message: response.extra.message,
|
||||
};
|
||||
|
||||
// Update transactions in IndexedDB
|
||||
const result = await updateTransactionInDB(transactionData);
|
||||
fetchOngoingTransactions()
|
||||
executeEvent("execute-get-new-block-trades", {})
|
||||
// Update transactions in IndexedDB
|
||||
const result = await updateTransactionInDB(transactionData);
|
||||
fetchOngoingTransactions();
|
||||
executeEvent("execute-get-new-block-trades", {});
|
||||
} catch (error) {
|
||||
console.log({error})
|
||||
console.log({ error });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -256,36 +244,33 @@ function App() {
|
||||
};
|
||||
}, [userInfo?.address]);
|
||||
|
||||
const getCoinLabel = useCallback((coin?: string)=> {
|
||||
switch(coin || selectedCoin){
|
||||
case "LITECOIN":{
|
||||
|
||||
return 'LTC'
|
||||
const getCoinLabel = useCallback(
|
||||
(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;
|
||||
}
|
||||
case "DOGECOIN":{
|
||||
|
||||
return 'DOGE'
|
||||
}
|
||||
case "BITCOIN":{
|
||||
|
||||
return 'BTC'
|
||||
}
|
||||
case "DIGIBYTE":{
|
||||
|
||||
return 'DGB'
|
||||
}
|
||||
case "RAVENCOIN":{
|
||||
|
||||
return 'RVN'
|
||||
}
|
||||
case "PIRATECHAIN":{
|
||||
|
||||
return 'ARRR'
|
||||
}
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}, [selectedCoin])
|
||||
},
|
||||
[selectedCoin]
|
||||
);
|
||||
|
||||
const gameContextValue: IContextProps = {
|
||||
userInfo,
|
||||
@ -296,37 +281,54 @@ function App() {
|
||||
fetchOngoingTransactions,
|
||||
foreignCoinBalance,
|
||||
qortBalance,
|
||||
isAuthenticated,
|
||||
isAuthenticated,
|
||||
setIsAuthenticated,
|
||||
OAuthLoading,
|
||||
OAuthLoading,
|
||||
setOAuthLoading,
|
||||
updateTransactionInDB,
|
||||
sellOrders,
|
||||
deleteTemporarySellOrder, updateTemporaryFailedTradeBots, fetchTemporarySellOrders, isUsingGateway, selectedCoin, setSelectedCoin, getCoinLabel
|
||||
deleteTemporarySellOrder,
|
||||
updateTemporaryFailedTradeBots,
|
||||
fetchTemporarySellOrders,
|
||||
isUsingGateway,
|
||||
selectedCoin,
|
||||
setSelectedCoin,
|
||||
getCoinLabel,
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<NotificationContext.Provider value={notificationContextValue}>
|
||||
<LoadingContext.Provider value={loadingContextValue}>
|
||||
<UserContext.Provider value={userContextValue}>
|
||||
<GameContext.Provider value={gameContextValue}>
|
||||
<Notification />
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<Routes>
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<HomePage
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</ThemeProvider>
|
||||
</GameContext.Provider>
|
||||
</UserContext.Provider>
|
||||
</LoadingContext.Provider>
|
||||
</NotificationContext.Provider>
|
||||
<GlobalProvider
|
||||
config={{
|
||||
auth: {
|
||||
balanceSetting: {
|
||||
interval: 180000,
|
||||
onlyOnMount: false,
|
||||
},
|
||||
authenticateOnMount: false,
|
||||
userAccountInfo: {
|
||||
address: userInfo?.address,
|
||||
publicKey: userInfo?.publicKey,
|
||||
},
|
||||
},
|
||||
appName: "q-trade",
|
||||
publicSalt: "SwQR6+uWzwm9Cqw1vqpR8zPKIThqdc5X7CUhWKn+rfc=",
|
||||
}}
|
||||
>
|
||||
<NotificationContext.Provider value={notificationContextValue}>
|
||||
<LoadingContext.Provider value={loadingContextValue}>
|
||||
<UserContext.Provider value={userContextValue}>
|
||||
<GameContext.Provider value={gameContextValue}>
|
||||
<Notification />
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
</Routes>
|
||||
</ThemeProvider>
|
||||
</GameContext.Provider>
|
||||
</UserContext.Provider>
|
||||
</LoadingContext.Provider>
|
||||
</NotificationContext.Provider>
|
||||
</GlobalProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { useIndexedDBContext } from "../../contexts/indexedDBContext";
|
||||
|
||||
const fetchTradeInfo = async (qortalAtAddress) => {
|
||||
|
||||
const checkIfOfferingRes = await fetch(`/crosschain/trade/${qortalAtAddress}`)
|
||||
const checkIfOfferingRes = await fetch(`http://devnet-nodes.qortal.link:11112/crosschain/trade/${qortalAtAddress}`)
|
||||
const data = await checkIfOfferingRes.json()
|
||||
return data
|
||||
};
|
||||
|
@ -24,7 +24,6 @@ export const BuyContainer = styled(Box)(({ theme }) => ({
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
bottom: "0px",
|
||||
height: "100px",
|
||||
padding: "18px 14px 12px 14px",
|
||||
background: "#323336",
|
||||
zIndex: 3,
|
||||
|
@ -10,12 +10,13 @@ import { AgGridReact } from "ag-grid-react";
|
||||
import {
|
||||
ColDef,
|
||||
RowClassParams,
|
||||
RowNode,
|
||||
RowStyle,
|
||||
SizeColumnsToContentStrategy,
|
||||
} from "ag-grid-community";
|
||||
import "ag-grid-community/styles/ag-grid.css";
|
||||
import "ag-grid-community/styles/ag-theme-alpine.css";
|
||||
import InfoOutlineIcon from '@mui/icons-material/InfoOutline';
|
||||
import InfoOutlineIcon from "@mui/icons-material/InfoOutline";
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
@ -26,6 +27,7 @@ import {
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
FormControlLabel,
|
||||
IconButton,
|
||||
Snackbar,
|
||||
SnackbarCloseReason,
|
||||
@ -48,9 +50,8 @@ import {
|
||||
MainContainer,
|
||||
} 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 = "devnet-nodes.qortal.link:11111";
|
||||
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
||||
@ -79,8 +80,12 @@ export const autoSizeStrategy: SizeColumnsToContentStrategy = {
|
||||
type: "fitCellContents",
|
||||
};
|
||||
|
||||
export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
export const TradeOffers: React.FC<any> = ({
|
||||
foreignCoinBalance,
|
||||
fee,
|
||||
}: any) => {
|
||||
const [offers, setOffers] = useState<any[]>([]);
|
||||
const [signedUnlockingFees, setSignedUnlockingFees] = useState(null);
|
||||
const [qortalNames, setQortalNames] = useState({});
|
||||
const {
|
||||
fetchOngoingTransactions,
|
||||
@ -90,6 +95,7 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
getCoinLabel,
|
||||
selectedCoin,
|
||||
} = useContext(gameContext);
|
||||
const isRemoveOrdersWithoutUnlockingFees = useRef(false);
|
||||
const listOfOngoingTradesAts = useMemo(() => {
|
||||
return (
|
||||
onGoingTrades
|
||||
@ -105,6 +111,14 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
message: messageInfo,
|
||||
} = useModal();
|
||||
|
||||
const {
|
||||
isShow: isShowTradesUnknownFee,
|
||||
onCancel: onCancelTradesUnknownFee,
|
||||
onOk: onOkTradesUnknownFee,
|
||||
show: showTradesUnknownFee,
|
||||
message: messageTradesUnknownFee,
|
||||
} = useModal();
|
||||
|
||||
const offersWithoutOngoing = useMemo(() => {
|
||||
return offers.filter(
|
||||
(item) => !listOfOngoingTradesAts.includes(item.qortalAtAddress)
|
||||
@ -122,13 +136,39 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
const offeringTrades = useRef<any[]>([]);
|
||||
const blockedTradesList = useRef([]);
|
||||
const gridRef = useRef<any>(null);
|
||||
const [openShowOfferDetails, setOpenShowOfferDetails] = useState(null)
|
||||
const [openShowOfferDetails, setOpenShowOfferDetails] = useState(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [info, setInfo] = useState<any>(null);
|
||||
const BuyButton = () => {
|
||||
return <BuyOrderBtn onClick={buyOrder}>BUY</BuyOrderBtn>;
|
||||
};
|
||||
|
||||
const intervalGetSignedUnlockingFees = useRef<number | null>(null);
|
||||
|
||||
const signedUnlockingFeesRef = useRef(signedUnlockingFees);
|
||||
const feeRef = useRef(fee);
|
||||
|
||||
const knownFees = useMemo(() => {
|
||||
const offersWithKnownFees = [];
|
||||
|
||||
selectedOffers.forEach((offer) => {
|
||||
const feeEntry = signedUnlockingFees.find(
|
||||
(item) => item?.atAddress === offer.qortalAtAddress
|
||||
);
|
||||
|
||||
if (feeEntry && typeof feeEntry.fee === "number") {
|
||||
offersWithKnownFees.push({ ...offer, fee: feeEntry.fee });
|
||||
}
|
||||
});
|
||||
const totalKnownFees = offersWithKnownFees.reduce((sum, offer) => {
|
||||
return sum + (offer.fee || 0);
|
||||
}, 0);
|
||||
const feeInLtc = totalKnownFees / 1e8;
|
||||
return +feeInLtc.toFixed(8);
|
||||
}, [selectedOffers, signedUnlockingFees]);
|
||||
|
||||
console.log("knownFees", knownFees);
|
||||
|
||||
const defaultColDef = {
|
||||
resizable: true, // Make columns resizable by default
|
||||
sortable: true, // Make columns sortable by default
|
||||
@ -176,8 +216,6 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const columnDefs: ColDef[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@ -189,12 +227,25 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
pinned: "left", // Optional, to pin this column on the left
|
||||
resizable: false,
|
||||
suppressRowClickSelection: true,
|
||||
|
||||
cellRenderer: (params) =>
|
||||
<SelectWithInfoCell {...params} selectTradeForDetails={()=> {
|
||||
setOpenShowOfferDetails(params?.node?.data)
|
||||
}} />,
|
||||
// suppressRowClickSelection: true, // prevent whole row selection on click
|
||||
|
||||
cellRenderer: (params) => (
|
||||
<SelectWithInfoCell
|
||||
{...params}
|
||||
selectTradeForDetails={() => {
|
||||
const hasSignedFee = signedUnlockingFees?.find(
|
||||
(item) =>
|
||||
item?.atAddress === params?.node?.data?.qortalAtAddress
|
||||
);
|
||||
let fee = null;
|
||||
if (hasSignedFee) {
|
||||
fee = hasSignedFee.fee;
|
||||
}
|
||||
|
||||
setOpenShowOfferDetails({ ...(params?.node?.data || {}), fee });
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// suppressRowClickSelection: true, // prevent whole row selection on click
|
||||
},
|
||||
{
|
||||
headerName: "QORT AMOUNT",
|
||||
@ -220,6 +271,22 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
minWidth: 150, // Ensure it doesn't shrink too much
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
headerName: `Unlocking fee`,
|
||||
flex: 1, // Flex makes this column responsive
|
||||
minWidth: 150, // Ensure it doesn't shrink too much
|
||||
resizable: true,
|
||||
valueGetter: (params) => {
|
||||
if (params?.data?.qortalAtAddress) {
|
||||
console.log("22signedUnlockingFees", signedUnlockingFees);
|
||||
const hasSignedFee = signedUnlockingFees?.find(
|
||||
(item) => item?.atAddress === params.data.qortalAtAddress
|
||||
);
|
||||
if (!hasSignedFee) return "Unknown";
|
||||
return hasSignedFee.fee;
|
||||
} else return "Unknown";
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: "Seller",
|
||||
field: "qortalCreator",
|
||||
@ -241,7 +308,7 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
},
|
||||
},
|
||||
];
|
||||
}, [qortalNames, getCoinLabel]);
|
||||
}, [qortalNames, getCoinLabel, signedUnlockingFees]);
|
||||
|
||||
// const onRowClicked = (event: any) => {
|
||||
// if(listOfOngoingTradesAts.includes(event.data.qortalAtAddress)) return
|
||||
@ -256,7 +323,7 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
|
||||
const getNewBlockedTrades = async () => {
|
||||
const unconfirmedTransactionsList = async () => {
|
||||
const unconfirmedTransactionslUrl = `/transactions/unconfirmed?txType=MESSAGE&limit=0&reverse=true`;
|
||||
const unconfirmedTransactionslUrl = `http://devnet-nodes.qortal.link:11112/transactions/unconfirmed?txType=MESSAGE&limit=0&reverse=true`;
|
||||
|
||||
var addBlockedTrades = JSON.parse(
|
||||
localStorage.getItem("failedTrades") || "[]"
|
||||
@ -468,7 +535,7 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if(isUsingGateway === null) return
|
||||
if (isUsingGateway === null) return;
|
||||
blockedTradesList.current = JSON.parse(
|
||||
localStorage.getItem("failedTrades") || "[]"
|
||||
);
|
||||
@ -489,8 +556,36 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
};
|
||||
}, [isUsingGateway]);
|
||||
|
||||
const getSignedUnlockingFees = useCallback(async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`http://devnet-nodes.qortal.link:11112/crosschain/signedfees`
|
||||
);
|
||||
const data = await response.json();
|
||||
if (data && Array.isArray(data)) {
|
||||
setSignedUnlockingFees(data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
console.log("signed", signedUnlockingFees);
|
||||
|
||||
useEffect(() => {
|
||||
if(isUsingGateway === null) return
|
||||
getSignedUnlockingFees();
|
||||
intervalGetSignedUnlockingFees.current = setInterval(() => {
|
||||
getSignedUnlockingFees();
|
||||
}, 150000);
|
||||
return () => {
|
||||
if (intervalGetSignedUnlockingFees.current) {
|
||||
clearInterval(intervalGetSignedUnlockingFees.current);
|
||||
}
|
||||
};
|
||||
}, [getSignedUnlockingFees]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isUsingGateway === null) return;
|
||||
if (selectedCoin === null) return;
|
||||
restartTradeOffers();
|
||||
setTimeout(() => {
|
||||
@ -504,13 +599,17 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
}, [isUsingGateway, selectedCoin]);
|
||||
|
||||
const selectedTotalLTC = useMemo(() => {
|
||||
return selectedOffers.reduce((acc: number, curr: any) => {
|
||||
const total = selectedOffers.reduce((acc: number, curr: any) => {
|
||||
return acc + (+curr.foreignAmount || 0); // Ensure qortAmount is defined
|
||||
}, 0);
|
||||
}, [selectedOffers]);
|
||||
|
||||
const totalWithKnownFees = +total + +knownFees;
|
||||
return totalWithKnownFees;
|
||||
}, [selectedOffers, knownFees]);
|
||||
|
||||
const buyOrder = async () => {
|
||||
try {
|
||||
isRemoveOrdersWithoutUnlockingFees.current = false;
|
||||
if (+foreignCoinBalance < +selectedTotalLTC.toFixed(4)) {
|
||||
setOpen(true);
|
||||
setInfo({
|
||||
@ -521,6 +620,36 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
}
|
||||
|
||||
if (selectedOffers?.length < 1) return;
|
||||
let offersWithKnownFees = [];
|
||||
const offersWithUnknownFees = [];
|
||||
|
||||
selectedOffers.forEach((offer) => {
|
||||
const feeEntry = signedUnlockingFees.find(
|
||||
(item) => item?.atAddress === offer.qortalAtAddress
|
||||
);
|
||||
|
||||
if (feeEntry && typeof feeEntry.fee === "number") {
|
||||
offersWithKnownFees.push({ ...offer, fee: feeEntry.fee });
|
||||
} else {
|
||||
offersWithUnknownFees.push(offer);
|
||||
}
|
||||
});
|
||||
console.log("offersWithKnownFees", offersWithKnownFees);
|
||||
console.log("offersWithUnknownFees", offersWithUnknownFees);
|
||||
if (offersWithUnknownFees?.length > 0) {
|
||||
await showTradesUnknownFee({
|
||||
message: "",
|
||||
});
|
||||
|
||||
if (!isRemoveOrdersWithoutUnlockingFees.current) {
|
||||
offersWithKnownFees = [
|
||||
...offersWithKnownFees,
|
||||
...offersWithUnknownFees,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
console.log("offersWithKnownFees", offersWithKnownFees);
|
||||
|
||||
setIsShowBuyInProgress({ status: "buying" });
|
||||
|
||||
@ -529,7 +658,7 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
// type: 'info',
|
||||
// message: "Attempting to submit buy order. Please wait..."
|
||||
// })
|
||||
const listOfATs = selectedOffers;
|
||||
const listOfATs = offersWithKnownFees;
|
||||
const response = await qortalRequestWithTimeout(
|
||||
{
|
||||
action: "CREATE_TRADE_BUY_ORDER",
|
||||
@ -609,6 +738,18 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
if (params.data.qortalAtAddress === selectedOffer?.qortalAtAddress) {
|
||||
return { background: "#6D94F533" };
|
||||
}
|
||||
|
||||
const hasSignedFee = signedUnlockingFees?.find(
|
||||
(item) => item?.atAddress === params.data.qortalAtAddress
|
||||
);
|
||||
console.log("hasSignedFee", hasSignedFee);
|
||||
if (hasSignedFee) {
|
||||
console.log("fee2fee", fee);
|
||||
if (fee && hasSignedFee?.fee > fee) {
|
||||
return { backgroundColor: "#ff6347" };
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
// const onGridReady = (params) => {
|
||||
@ -639,9 +780,10 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
}, []);
|
||||
|
||||
const selectedTotalQORT = useMemo(() => {
|
||||
return selectedOffers.reduce((acc: number, curr: any) => {
|
||||
const total = selectedOffers.reduce((acc: number, curr: any) => {
|
||||
return acc + (+curr.qortAmount || 0); // Ensure qortAmount is defined
|
||||
}, 0);
|
||||
return total;
|
||||
}, [selectedOffers]);
|
||||
|
||||
const onGridReady = useCallback((params: any) => {
|
||||
@ -664,6 +806,32 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
setInfo(null);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
signedUnlockingFeesRef.current = signedUnlockingFees;
|
||||
feeRef.current = fee;
|
||||
|
||||
if (gridRef.current?.api) {
|
||||
console.log("Total rows:", gridRef.current.api.getDisplayedRowCount());
|
||||
|
||||
gridRef.current.api.forEachNode((rowNode: RowNode) => {
|
||||
const qortalAtAddress = rowNode.data?.qortalAtAddress;
|
||||
const hasSignedFee = signedUnlockingFeesRef.current?.find(
|
||||
(item) => item?.atAddress === qortalAtAddress
|
||||
);
|
||||
|
||||
const isSelectable =
|
||||
!hasSignedFee || hasSignedFee.fee <= feeRef.current;
|
||||
|
||||
rowNode.setRowSelectable(isSelectable); // ✅ apply logic per row
|
||||
});
|
||||
|
||||
// Optional: refresh selection/checkbox visuals
|
||||
gridRef.current.api.refreshCells({ force: true });
|
||||
}
|
||||
}, [signedUnlockingFees, fee]);
|
||||
console.log("signedUnlockingFees", signedUnlockingFees, fee);
|
||||
if (!signedUnlockingFees || !fee) return null;
|
||||
|
||||
return (
|
||||
<MainContainer>
|
||||
<div
|
||||
@ -688,6 +856,23 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
onGridReady={onGridReady}
|
||||
// domLayout='autoHeight'
|
||||
getRowId={(params) => params.data.qortalAtAddress} // Ensure rows have unique IDs
|
||||
gridOptions={{
|
||||
isRowSelectable: (params) => {
|
||||
let selectable = true;
|
||||
const hasSignedFee = signedUnlockingFeesRef.current?.find(
|
||||
(item) => item?.atAddress === params.data.qortalAtAddress
|
||||
);
|
||||
if (!hasSignedFee) selectable = true;
|
||||
console.log(
|
||||
"fee",
|
||||
feeRef.current,
|
||||
signedUnlockingFeesRef.current
|
||||
);
|
||||
if (hasSignedFee && hasSignedFee?.fee > feeRef.current)
|
||||
selectable = false;
|
||||
return selectable;
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{/* {selectedOffer && (
|
||||
<Button onClick={buyOrder}>Buy</Button>
|
||||
@ -696,7 +881,7 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
height: "120px",
|
||||
height: "150px",
|
||||
}}
|
||||
/>
|
||||
<BuyContainer>
|
||||
@ -716,7 +901,7 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
width: "calc(100% - 75px)",
|
||||
}}
|
||||
>
|
||||
{selectedTotalQORT?.toFixed(3)} QORT
|
||||
{selectedTotalQORT?.toFixed(8)} QORT
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
@ -729,15 +914,17 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
color: selectedTotalLTC > foreignCoinBalance ? "red" : "white",
|
||||
backgroundColor:
|
||||
selectedTotalLTC > foreignCoinBalance ? "red" : "unset",
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
<span>{selectedTotalLTC?.toFixed(4)}</span>{" "}
|
||||
<span>{selectedTotalLTC?.toFixed(8)}</span>{" "}
|
||||
<span
|
||||
style={{
|
||||
marginLeft: "auto",
|
||||
}}
|
||||
>{`${getCoinLabel()} `}</span>
|
||||
>{`${getCoinLabel()} (with known fees)`}</span>
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography
|
||||
@ -746,7 +933,7 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
<span>{foreignCoinBalance?.toFixed(4)}</span>{" "}
|
||||
<span>{foreignCoinBalance?.toFixed(8)}</span>{" "}
|
||||
<span
|
||||
style={{
|
||||
marginLeft: "auto",
|
||||
@ -814,6 +1001,81 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)}
|
||||
{isShowTradesUnknownFee && (
|
||||
<Dialog
|
||||
open={isShowTradesUnknownFee}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">Warning</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText
|
||||
id="alert-dialog-description"
|
||||
sx={{ color: "white" }}
|
||||
>
|
||||
Some of your buy orders have unknown unlocking fees.
|
||||
</DialogContentText>
|
||||
<Spacer height="20px" />
|
||||
<DialogContentText
|
||||
id="alert-dialog-description"
|
||||
sx={{ color: "white" }}
|
||||
>
|
||||
You may proceed with your purchases without removing these orders,
|
||||
but there is a higher risk that the trades may not go through. In
|
||||
such cases, you will be refunded.
|
||||
</DialogContentText>
|
||||
|
||||
<Spacer height="20px" />
|
||||
<FormControlLabel
|
||||
sx={{
|
||||
margin: 0,
|
||||
}}
|
||||
control={
|
||||
<Checkbox
|
||||
onChange={(e) =>
|
||||
(isRemoveOrdersWithoutUnlockingFees.current =
|
||||
e.target.checked)
|
||||
}
|
||||
edge="start"
|
||||
tabIndex={-1}
|
||||
disableRipple
|
||||
sx={{
|
||||
"&.Mui-checked": {
|
||||
color: "white",
|
||||
},
|
||||
"& .MuiSvgIcon-root": {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
<Typography sx={{ fontSize: "14px" }}>
|
||||
Remove orders with unknown unlocking fees
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={onCancelTradesUnknownFee}
|
||||
autoFocus
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={onOkTradesUnknownFee}
|
||||
autoFocus
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)}
|
||||
{isShowBuyInProgress && (
|
||||
<Dialog
|
||||
open={isShowBuyInProgress}
|
||||
@ -945,60 +1207,111 @@ export const TradeOffers: React.FC<any> = ({ foreignCoinBalance }: any) => {
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)}
|
||||
<Dialog
|
||||
open={!!openShowOfferDetails}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: "rgb(39, 40, 44)",
|
||||
background: "rgb(39, 40, 44)",
|
||||
},
|
||||
<Dialog
|
||||
open={!!openShowOfferDetails}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: "rgb(39, 40, 44)",
|
||||
background: "rgb(39, 40, 44)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle
|
||||
sx={{
|
||||
maxHeight: "calc(90vh - 55px)",
|
||||
maxWidth: "90%",
|
||||
background: "rgb(39, 40, 44)",
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
<DialogTitle
|
||||
sx={{
|
||||
maxHeight: "calc(90vh - 55px)",
|
||||
maxWidth: "90%",
|
||||
background: "rgb(39, 40, 44)",
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
<Typography variant="subtitle1">
|
||||
Buy {openShowOfferDetails?.qortAmount} QORT @ {openShowOfferDetails?.foreignAmount} {getCoinLabel()}
|
||||
</Typography>
|
||||
|
||||
</DialogTitle>
|
||||
<IconButton
|
||||
<Typography variant="subtitle1">
|
||||
Buy {openShowOfferDetails?.qortAmount} QORT @{" "}
|
||||
{openShowOfferDetails?.foreignAmount} {getCoinLabel()}
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<IconButton
|
||||
aria-label="close"
|
||||
onClick={()=> setOpenShowOfferDetails(null)}
|
||||
onClick={() => setOpenShowOfferDetails(null)}
|
||||
sx={{ position: "absolute", right: 8, top: 8, color: "#fff" }}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<DialogContent dividers sx={{ borderColor: "#333" }}>
|
||||
<TradeRow enableSlice enableCopy label="Seller" value={openShowOfferDetails?.qortalCreator} extra={qortalNames[openShowOfferDetails?.qortalCreator]} />
|
||||
<TradeRow label="Amount" value={`${openShowOfferDetails?.qortAmount} QORT`} />
|
||||
<TradeRow label="Total" value={`${openShowOfferDetails?.foreignAmount} ${getCoinLabel()}`} />
|
||||
<TradeRow label="Price" value={`${+openShowOfferDetails?.foreignAmount / +openShowOfferDetails?.qortAmount } ${getCoinLabel()}/QORT`} />
|
||||
<TradeRow enableSlice enableCopy label="AT Address" value={openShowOfferDetails?.qortalAtAddress} />
|
||||
</DialogContent>
|
||||
<DialogActions
|
||||
sx={{
|
||||
background: "rgb(39, 40, 44)",
|
||||
<DialogContent dividers sx={{ borderColor: "#333" }}>
|
||||
{fee &&
|
||||
openShowOfferDetails?.fee &&
|
||||
+fee < +openShowOfferDetails?.fee && (
|
||||
<Box
|
||||
sx={{
|
||||
background: "red",
|
||||
padding: "10px",
|
||||
borderRadius: "5px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ color: "white" }}>
|
||||
The unlocking fee on this node is lower than the amount
|
||||
required for this order.
|
||||
</Typography>
|
||||
<Spacer height="10px" />
|
||||
<Typography sx={{ color: "white" }}>
|
||||
If you're using your own node, you can change the fee by
|
||||
clicking the "Fee" button next to the coin selector.
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<TradeRow
|
||||
enableSlice
|
||||
enableCopy
|
||||
label="Seller"
|
||||
value={openShowOfferDetails?.qortalCreator}
|
||||
extra={qortalNames[openShowOfferDetails?.qortalCreator]}
|
||||
/>
|
||||
<TradeRow
|
||||
label="Amount"
|
||||
value={`${openShowOfferDetails?.qortAmount} QORT`}
|
||||
/>
|
||||
<TradeRow
|
||||
label="Total"
|
||||
value={`${openShowOfferDetails?.foreignAmount} ${getCoinLabel()}`}
|
||||
/>
|
||||
<TradeRow
|
||||
label="Price"
|
||||
value={`${
|
||||
+openShowOfferDetails?.foreignAmount /
|
||||
+openShowOfferDetails?.qortAmount
|
||||
} ${getCoinLabel()}/QORT`}
|
||||
/>
|
||||
{openShowOfferDetails?.fee && (
|
||||
<TradeRow
|
||||
label="Unlocking fee"
|
||||
value={`${openShowOfferDetails?.fee} sats`}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TradeRow
|
||||
enableSlice
|
||||
enableCopy
|
||||
label="AT Address"
|
||||
value={openShowOfferDetails?.qortalAtAddress}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions
|
||||
sx={{
|
||||
background: "rgb(39, 40, 44)",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
setOpenShowOfferDetails(null);
|
||||
}}
|
||||
autoFocus
|
||||
>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
setOpenShowOfferDetails(null)
|
||||
}}
|
||||
autoFocus
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</MainContainer>
|
||||
);
|
||||
};
|
||||
@ -1008,13 +1321,13 @@ const TradeRow = ({
|
||||
value,
|
||||
extra,
|
||||
enableSlice,
|
||||
enableCopy
|
||||
enableCopy,
|
||||
}: {
|
||||
label: string;
|
||||
value: string;
|
||||
extra?: string;
|
||||
enableSlice?: boolean
|
||||
enableCopy?: boolean
|
||||
enableSlice?: boolean;
|
||||
enableCopy?: boolean;
|
||||
}) => (
|
||||
<Box
|
||||
sx={{
|
||||
@ -1036,10 +1349,12 @@ const TradeRow = ({
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" sx={{ fontWeight: 500 }}>
|
||||
{enableSlice && value?.length > 18 ? value?.slice(0, 6) + "..." + value.slice(-4) : value}
|
||||
{enableSlice && value?.length > 18
|
||||
? value?.slice(0, 6) + "..." + value.slice(-4)
|
||||
: value}
|
||||
</Typography>
|
||||
{enableCopy && (
|
||||
<Tooltip title="Copy">
|
||||
<Tooltip title="Copy">
|
||||
<IconButton size="small" onClick={() => copyToClipboard(value)}>
|
||||
<ContentCopyIcon fontSize="small" />
|
||||
</IconButton>
|
||||
@ -1054,34 +1369,30 @@ const TradeRow = ({
|
||||
</Box>
|
||||
);
|
||||
|
||||
|
||||
const SelectWithInfoCell = ({selectTradeForDetails}) => {
|
||||
|
||||
const SelectWithInfoCell = ({ selectTradeForDetails }) => {
|
||||
const handleInfoClick = (e: React.MouseEvent) => {
|
||||
e.stopPropagation(); // Prevents row selection
|
||||
selectTradeForDetails()
|
||||
selectTradeForDetails();
|
||||
// alert(`Info for ${data.qortalAtAddress}`); // Replace with your own UI
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="ag-cell-ignore-selection" style={{ display: "flex", alignItems: "center", gap: 6 }}>
|
||||
|
||||
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={handleInfoClick}
|
||||
onClickCapture={(e) => {
|
||||
e.stopPropagation();
|
||||
handleInfoClick(e)
|
||||
<div
|
||||
className="ag-cell-ignore-selection"
|
||||
style={{ display: "flex", alignItems: "center", gap: 6 }}
|
||||
>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={handleInfoClick}
|
||||
onClickCapture={(e) => {
|
||||
e.stopPropagation();
|
||||
handleInfoClick(e);
|
||||
}}
|
||||
onMouseDown={(e) => e.stopPropagation()} // 👈 this is key
|
||||
sx={{ minWidth: 0, padding: "0 4px" }}
|
||||
>
|
||||
<InfoOutlineIcon />
|
||||
</IconButton>
|
||||
|
||||
onMouseDown={(e) => e.stopPropagation()} // 👈 this is key
|
||||
sx={{ minWidth: 0, padding: "0 4px" }}
|
||||
>
|
||||
<InfoOutlineIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
@ -214,10 +214,12 @@ export const CoinReceiveBtn = styled(Button)(({ theme }) => ({
|
||||
|
||||
export const CoinSelectRow = styled(Box)({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexDirection: "row",
|
||||
gap: "5px",
|
||||
alignSelf: "flex-start",
|
||||
marginBottom: "5px"
|
||||
justifyContent: "flex-start",
|
||||
marginBottom: "5px",
|
||||
width: '100%',
|
||||
flexWrap: 'wrap'
|
||||
});
|
||||
|
||||
export const CoinActionContainer = styled(Box)({
|
||||
|
@ -1,4 +1,11 @@
|
||||
import { useState, useEffect, useRef, useContext, ChangeEvent, useMemo } from "react";
|
||||
import {
|
||||
useState,
|
||||
useEffect,
|
||||
useRef,
|
||||
useContext,
|
||||
ChangeEvent,
|
||||
useMemo,
|
||||
} from "react";
|
||||
import {
|
||||
BubbleCardColored1,
|
||||
CoinActionContainer,
|
||||
@ -26,15 +33,15 @@ 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 {FallingLines} from 'react-loader-spinner'
|
||||
import { AddressQRCode } from "./AddressQRCode";
|
||||
import { FallingLines } from "react-loader-spinner";
|
||||
import {
|
||||
Alert,
|
||||
AppBar,
|
||||
Avatar,
|
||||
Box,
|
||||
ButtonBase,
|
||||
Card,
|
||||
CardContent,
|
||||
FormControl,
|
||||
@ -58,6 +65,8 @@ import arrrIcon from "../../assets/img/arrr.png";
|
||||
import { Spacer } from "../common/Spacer";
|
||||
import { ReusableModal } from "../common/reusable-modal/ReusableModal";
|
||||
import { NotificationContext } from "../../contexts/notificationContext";
|
||||
import UnsignedFees from "../sell/UnsignedFees";
|
||||
import { FeeManager } from "../sell/FeeManager";
|
||||
|
||||
const checkIfLocal = async () => {
|
||||
try {
|
||||
@ -140,7 +149,13 @@ const SelectRow = ({ coin }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Header = ({ qortBalance, foreignCoinBalance }: any) => {
|
||||
export const Header = ({
|
||||
qortBalance,
|
||||
foreignCoinBalance,
|
||||
qortAddress,
|
||||
fee,
|
||||
setFee
|
||||
}: any) => {
|
||||
const [openDropdown, setOpenDropdown] = useState<boolean>(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const buttonRef = useRef<HTMLDivElement>(null);
|
||||
@ -167,7 +182,6 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => {
|
||||
useContext(gameContext);
|
||||
const { setNotification } = useContext(NotificationContext);
|
||||
|
||||
|
||||
const LocalNodeSwitch = styled(Switch)(({ theme }) => ({
|
||||
padding: 8,
|
||||
"& .MuiSwitch-track": {
|
||||
@ -239,31 +253,32 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => {
|
||||
// }
|
||||
// }, [userInfo]);
|
||||
|
||||
const sendCoin = async ()=> {
|
||||
const sendCoin = async () => {
|
||||
try {
|
||||
const coin = openCoinActionModal.coin === "QORT" ? 'QORT' : getCoinLabel()
|
||||
if(!coin) return
|
||||
const coin =
|
||||
openCoinActionModal.coin === "QORT" ? "QORT" : getCoinLabel();
|
||||
if (!coin) return;
|
||||
setOpen(true);
|
||||
setInfo({
|
||||
type: "info",
|
||||
message: "Sending Coin...",
|
||||
autoHideDurationOff: true
|
||||
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);
|
||||
amount: +amount,
|
||||
});
|
||||
if (response?.error) {
|
||||
throw new Error(response?.error || "Failed to send coin.");
|
||||
}
|
||||
setOpen(true);
|
||||
setInfo({
|
||||
type: "success",
|
||||
message: "Coin sent",
|
||||
});
|
||||
setAmount('')
|
||||
setAmount("");
|
||||
} catch (error) {
|
||||
setOpen(true);
|
||||
setInfo({
|
||||
@ -271,7 +286,7 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => {
|
||||
message: error?.error || error?.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -428,12 +443,10 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => {
|
||||
}}
|
||||
/>
|
||||
{foreignCoinBalance === null ? (
|
||||
<FallingLines
|
||||
color="white"
|
||||
width="30"
|
||||
visible={true}
|
||||
/>
|
||||
) : foreignCoinBalance}{" "}
|
||||
<FallingLines color="white" width="30" visible={true} />
|
||||
) : (
|
||||
foreignCoinBalance
|
||||
)}{" "}
|
||||
{getCoinLabel()}
|
||||
</Box>
|
||||
<CoinActionsRow>
|
||||
@ -467,7 +480,10 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => {
|
||||
<Select
|
||||
size="small"
|
||||
value={selectedCoin}
|
||||
onChange={(e) => setSelectedCoin(e.target.value)}
|
||||
onChange={(e) => {
|
||||
setFee(null)
|
||||
setSelectedCoin(e.target.value)
|
||||
}}
|
||||
>
|
||||
<MenuItem value={"LITECOIN"}>
|
||||
<SelectRow coin="LTC" />
|
||||
@ -488,6 +504,16 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => {
|
||||
<SelectRow coin="ARRR" />
|
||||
</MenuItem>
|
||||
</Select>
|
||||
{!isUsingGateway && selectedCoin !== 'PIRATECHAIN' && (
|
||||
<>
|
||||
<FeeManager selectedCoin={selectedCoin} fee={fee}
|
||||
setFee={setFee} />
|
||||
<UnsignedFees
|
||||
qortAddress={qortAddress}
|
||||
selectedCoin={selectedCoin}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</CoinSelectRow>
|
||||
<Snackbar
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
|
||||
@ -497,252 +523,286 @@ export const Header = ({ qortBalance, foreignCoinBalance }: any) => {
|
||||
>
|
||||
{info?.type && (
|
||||
<Alert
|
||||
onClose={handleClose}
|
||||
severity={info?.type}
|
||||
variant="filled"
|
||||
sx={{ width: "100%" }}
|
||||
>
|
||||
{info?.message}
|
||||
</Alert>
|
||||
onClose={handleClose}
|
||||
severity={info?.type}
|
||||
variant="filled"
|
||||
sx={{ width: "100%" }}
|
||||
>
|
||||
{info?.message}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
</Snackbar>
|
||||
{openCoinActionModal && (
|
||||
<ReusableModal
|
||||
onClickClose={() => {
|
||||
setOpenCoinActionModal(null);
|
||||
setAmount('')
|
||||
setSenderAddress('')
|
||||
setAmount("");
|
||||
setSenderAddress("");
|
||||
}}
|
||||
backdrop
|
||||
>
|
||||
<CoinActionContainer>
|
||||
{openCoinActionModal.type === "send" ? <>
|
||||
<CoinActionRow>
|
||||
<HeaderRow>
|
||||
{openCoinActionModal.type === "send" &&
|
||||
openCoinActionModal.coin === "QORT" ? (
|
||||
<>
|
||||
<SendFont>Send {openCoinActionModal.coin}</SendFont>
|
||||
<img
|
||||
src={qortIcon}
|
||||
style={{
|
||||
height: "25px",
|
||||
width: "auto",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : openCoinActionModal.type === "send" &&
|
||||
openCoinActionModal.coin !== "QORT" ? (
|
||||
<>
|
||||
<SendFont>Send {openCoinActionModal.coin}</SendFont>
|
||||
<img
|
||||
src={getCoinIcon(getCoinLabel())}
|
||||
style={{
|
||||
height: "25px",
|
||||
width: "auto",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</HeaderRow>
|
||||
</CoinActionRow>
|
||||
<CoinActionRow>
|
||||
<FormControl fullWidth>
|
||||
<CustomInputField
|
||||
style={{ flexGrow: 1 }}
|
||||
name={
|
||||
openCoinActionModal.type === "send"
|
||||
? `${openCoinActionModal.coin === "QORT" ? 'Recipient Address or Name' : 'Recipient Address'}`
|
||||
: "Receive Address"
|
||||
}
|
||||
label={
|
||||
openCoinActionModal.type === "send"
|
||||
? `${openCoinActionModal.coin === "QORT" ? 'Recipient Address or Name' : 'Recipient Address'}`
|
||||
: "Receive Address"
|
||||
}
|
||||
variant="filled"
|
||||
value={
|
||||
openCoinActionModal.type === "send"
|
||||
? senderAddress
|
||||
: receiverAddress
|
||||
}
|
||||
required
|
||||
onChange={(e) => {
|
||||
if (openCoinActionModal.type === "send") {
|
||||
setSenderAddress(e.target.value);
|
||||
} else {
|
||||
setReceiverAddress(e.target.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</CoinActionRow>
|
||||
{openCoinActionModal.type === "send" && (
|
||||
<CoinActionRow>
|
||||
<FormControl fullWidth>
|
||||
<CustomInputField
|
||||
style={{ flexGrow: 1 }}
|
||||
name="Amount"
|
||||
label="Amount"
|
||||
variant="filled"
|
||||
type="number"
|
||||
value={
|
||||
amount
|
||||
}
|
||||
required
|
||||
onChange={(e) => {
|
||||
setAmount(e.target.value)
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</CoinActionRow>
|
||||
)}
|
||||
</> : (
|
||||
{openCoinActionModal.type === "send" ? (
|
||||
<>
|
||||
<ReceiveCoin setOpen={setOpen} setInfo={setInfo} coinAddresses={coinAddresses} setCoinAddresses={setCoinAddresses} selectedCoin={openCoinActionModal.coin === "QORT" ? 'QORT' :getCoinLabel()} />
|
||||
<CoinActionRow>
|
||||
<HeaderRow>
|
||||
{openCoinActionModal.type === "send" &&
|
||||
openCoinActionModal.coin === "QORT" ? (
|
||||
<>
|
||||
<SendFont>Send {openCoinActionModal.coin}</SendFont>
|
||||
<img
|
||||
src={qortIcon}
|
||||
style={{
|
||||
height: "25px",
|
||||
width: "auto",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : openCoinActionModal.type === "send" &&
|
||||
openCoinActionModal.coin !== "QORT" ? (
|
||||
<>
|
||||
<SendFont>Send {openCoinActionModal.coin}</SendFont>
|
||||
<img
|
||||
src={getCoinIcon(getCoinLabel())}
|
||||
style={{
|
||||
height: "25px",
|
||||
width: "auto",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</HeaderRow>
|
||||
</CoinActionRow>
|
||||
<CoinActionRow>
|
||||
<FormControl fullWidth>
|
||||
<CustomInputField
|
||||
style={{ flexGrow: 1 }}
|
||||
name={
|
||||
openCoinActionModal.type === "send"
|
||||
? `${
|
||||
openCoinActionModal.coin === "QORT"
|
||||
? "Recipient Address or Name"
|
||||
: "Recipient Address"
|
||||
}`
|
||||
: "Receive Address"
|
||||
}
|
||||
label={
|
||||
openCoinActionModal.type === "send"
|
||||
? `${
|
||||
openCoinActionModal.coin === "QORT"
|
||||
? "Recipient Address or Name"
|
||||
: "Recipient Address"
|
||||
}`
|
||||
: "Receive Address"
|
||||
}
|
||||
variant="filled"
|
||||
value={
|
||||
openCoinActionModal.type === "send"
|
||||
? senderAddress
|
||||
: receiverAddress
|
||||
}
|
||||
required
|
||||
onChange={(e) => {
|
||||
if (openCoinActionModal.type === "send") {
|
||||
setSenderAddress(e.target.value);
|
||||
} else {
|
||||
setReceiverAddress(e.target.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</CoinActionRow>
|
||||
{openCoinActionModal.type === "send" && (
|
||||
<CoinActionRow>
|
||||
<FormControl fullWidth>
|
||||
<CustomInputField
|
||||
style={{ flexGrow: 1 }}
|
||||
name="Amount"
|
||||
label="Amount"
|
||||
variant="filled"
|
||||
type="number"
|
||||
value={amount}
|
||||
required
|
||||
onChange={(e) => {
|
||||
setAmount(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</CoinActionRow>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ReceiveCoin
|
||||
setOpen={setOpen}
|
||||
setInfo={setInfo}
|
||||
coinAddresses={coinAddresses}
|
||||
setCoinAddresses={setCoinAddresses}
|
||||
selectedCoin={
|
||||
openCoinActionModal.coin === "QORT"
|
||||
? "QORT"
|
||||
: getCoinLabel()
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{openCoinActionModal.type === 'send' && (
|
||||
<CoinActionRow style={{gap: "10px"}}>
|
||||
{/* <CoinCancelBtn onClick={() => setOpenCoinActionModal(null)}>
|
||||
{openCoinActionModal.type === "send" && (
|
||||
<CoinActionRow style={{ gap: "10px" }}>
|
||||
{/* <CoinCancelBtn onClick={() => setOpenCoinActionModal(null)}>
|
||||
Cancel
|
||||
</CoinCancelBtn> */}
|
||||
<CoinConfirmSendBtn
|
||||
onClick={() => {
|
||||
if(openCoinActionModal.type === 'send'){
|
||||
sendCoin()
|
||||
}
|
||||
setNotification({
|
||||
alertType: "alertInfo",
|
||||
msg: "Sending...",
|
||||
});
|
||||
}}
|
||||
>
|
||||
{openCoinActionModal.type === "send" ? "Send" : "Receive"}
|
||||
</CoinConfirmSendBtn>
|
||||
</CoinActionRow>
|
||||
<CoinConfirmSendBtn
|
||||
onClick={() => {
|
||||
if (openCoinActionModal.type === "send") {
|
||||
sendCoin();
|
||||
}
|
||||
setNotification({
|
||||
alertType: "alertInfo",
|
||||
msg: "Sending...",
|
||||
});
|
||||
}}
|
||||
>
|
||||
{openCoinActionModal.type === "send" ? "Send" : "Receive"}
|
||||
</CoinConfirmSendBtn>
|
||||
</CoinActionRow>
|
||||
)}
|
||||
|
||||
|
||||
|
||||
</CoinActionContainer>
|
||||
</ReusableModal>
|
||||
)}
|
||||
</HeaderNav>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
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 {
|
||||
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 ReceiveCoin = ({coinAddresses, setCoinAddresses, selectedCoin, setOpen, setInfo})=> {
|
||||
const [errorMsg, setErrorMsg] = useState('')
|
||||
const foreignAddress = useMemo(()=> {
|
||||
return coinAddresses[selectedCoin] || null
|
||||
}, [coinAddresses, selectedCoin])
|
||||
|
||||
const getForeignAddress = async (coin)=> {
|
||||
const getForeignAddress = async (coin) => {
|
||||
try {
|
||||
setOpen(true);
|
||||
setInfo({
|
||||
type: "info",
|
||||
message: "Retrieving address...",
|
||||
});
|
||||
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.")
|
||||
}
|
||||
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)
|
||||
setErrorMsg(error?.message);
|
||||
} finally {
|
||||
setOpen(false);
|
||||
setInfo(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(()=> {
|
||||
if(!selectedCoin) return
|
||||
if(!coinAddresses[selectedCoin]){
|
||||
getForeignAddress(selectedCoin)
|
||||
useEffect(() => {
|
||||
if (!selectedCoin) return;
|
||||
if (!coinAddresses[selectedCoin]) {
|
||||
getForeignAddress(selectedCoin);
|
||||
}
|
||||
}, [selectedCoin, coinAddresses])
|
||||
}, [selectedCoin, coinAddresses]);
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<Typography sx={{
|
||||
color: 'white'
|
||||
}}>{`Send ${selectedCoin} to your address below`}</Typography>
|
||||
<Spacer height="20px" />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
>{`Send ${selectedCoin} to your address below`}</Typography>
|
||||
<Spacer height="20px" />
|
||||
{foreignAddress && (
|
||||
<CopyToClipboard text={foreignAddress} onCopy={()=> {
|
||||
setOpen(true);
|
||||
setInfo({
|
||||
type: "info",
|
||||
message: "Address copied!",
|
||||
});
|
||||
}}>
|
||||
<AddressBox>
|
||||
{foreignAddress} <img src={Copy} />
|
||||
</AddressBox>
|
||||
</CopyToClipboard>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
navigator.clipboard
|
||||
.writeText(foreignAddress)
|
||||
.then(() => {
|
||||
setOpen(true);
|
||||
setInfo({
|
||||
type: "info",
|
||||
message: "Address copied!",
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Failed to copy LTC address:", err);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<AddressBox>
|
||||
{foreignAddress} <img src={Copy} />
|
||||
</AddressBox>
|
||||
</ButtonBase>
|
||||
)}
|
||||
{foreignAddress && (
|
||||
<>
|
||||
<AddressQRCode targetAddress={foreignAddress} />
|
||||
<AddressQRCode targetAddress={foreignAddress} />
|
||||
</>
|
||||
)}
|
||||
{errorMsg && (
|
||||
<>
|
||||
<Spacer height="20px" />
|
||||
<Typography sx={{
|
||||
color: 'white'
|
||||
}}>{errorMsg}</Typography>
|
||||
<Spacer height="20px" />
|
||||
<Typography
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
{errorMsg}
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -23,8 +23,8 @@ import TradeBotList from "./TradeBotList";
|
||||
export const CustomLabel = styled(InputLabel)`
|
||||
font-weight: 400;
|
||||
font-family: Inter;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.2;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
`;
|
||||
|
||||
@ -61,12 +61,12 @@ export const CustomInput = styled(TextField)({
|
||||
// backgroundColor: "rgba(30, 30, 32, 1)",
|
||||
outline: "none",
|
||||
input: {
|
||||
fontSize: 10,
|
||||
fontSize: '14px',
|
||||
fontFamily: "Inter",
|
||||
fontWeight: 400,
|
||||
color: "white",
|
||||
"&::placeholder": {
|
||||
fontSize: 16,
|
||||
fontSize: '14px',
|
||||
color: "rgba(255, 255, 255, 0.2)",
|
||||
},
|
||||
outline: "none",
|
||||
@ -263,7 +263,7 @@ export const CreateSell = ({ qortAddress, show }) => {
|
||||
onChange={(e) => setQortAmount(+e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<Spacer height="6px" />
|
||||
<Spacer height="15px" />
|
||||
<CustomLabel htmlFor="standard-adornment-amount">
|
||||
{`Price of Each QORT (in ${getCoinLabel()})`}
|
||||
</CustomLabel>
|
||||
@ -275,7 +275,7 @@ export const CreateSell = ({ qortAddress, show }) => {
|
||||
onChange={(e) => setForeignAmount(+e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<Spacer height="6px" />
|
||||
<Spacer height="15px" />
|
||||
<Typography>
|
||||
{`${qortAmount * foreignAmount} ${getCoinLabel()}`} for{" "}
|
||||
{qortAmount} QORT
|
||||
|
374
src/components/sell/FeeManager.tsx
Normal file
374
src/components/sell/FeeManager.tsx
Normal file
@ -0,0 +1,374 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import gameContext from "../../contexts/gameContext";
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
ButtonBase,
|
||||
Snackbar,
|
||||
SnackbarCloseReason,
|
||||
ToggleButton,
|
||||
ToggleButtonGroup,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import ChangeCircleIcon from "@mui/icons-material/ChangeCircle";
|
||||
import { ReusableModal } from "../common/reusable-modal/ReusableModal";
|
||||
import QuestionMarkIcon from "@mui/icons-material/QuestionMark";
|
||||
import {
|
||||
CoinActionContainer,
|
||||
CoinActionRow,
|
||||
HeaderRow,
|
||||
} from "../header/Header-styles";
|
||||
import { CustomInput, CustomLabel } from "./CreateSell";
|
||||
import { Spacer } from "../common/Spacer";
|
||||
import { usePublish, Service, QortalGetMetadata } from "qapp-core";
|
||||
import { SetLeftFeature } from "ag-grid-community";
|
||||
|
||||
function calculateFeeFromRate(feePerKb, sizeInBytes) {
|
||||
const fee = (feePerKb / 1000) * sizeInBytes;
|
||||
return fee?.toFixed(0);
|
||||
}
|
||||
|
||||
function calculateRateFromFee(totalFee, sizeInBytes) {
|
||||
return (totalFee / sizeInBytes) * 1000;
|
||||
}
|
||||
|
||||
export const FeeManager = ({ selectedCoin, setFee, fee }) => {
|
||||
const [feeLocation, setFeeLocation] = useState<QortalGetMetadata>({
|
||||
name: "",
|
||||
identifier: "",
|
||||
service: "JSON",
|
||||
});
|
||||
const { resource } = usePublish(3, "JSON", feeLocation);
|
||||
const [editFee, setEditFee] = useState("");
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [recommendedFee, setRecommendedFee] = useState("m");
|
||||
const [openAlert, setOpenAlert] = useState(false);
|
||||
const [info, setInfo] = useState<any>(null);
|
||||
const { getCoinLabel } = useContext(gameContext);
|
||||
console.log("editFee", editFee);
|
||||
const handleCloseAlert = (
|
||||
event?: React.SyntheticEvent | Event,
|
||||
reason?: SnackbarCloseReason
|
||||
) => {
|
||||
if (reason === "clickaway") {
|
||||
return;
|
||||
}
|
||||
|
||||
setOpenAlert(false);
|
||||
setInfo(null);
|
||||
};
|
||||
const coin = useMemo(() => {
|
||||
return getCoinLabel(selectedCoin)?.toLowerCase();
|
||||
}, [selectedCoin, getCoinLabel]);
|
||||
|
||||
const establishUpdateFeeForm = useCallback(async (coin) => {
|
||||
setFee("");
|
||||
// if the coin or type is not set, then abort
|
||||
if (!coin) {
|
||||
return;
|
||||
}
|
||||
console.log("selectedCoin", coin);
|
||||
// const coinRequest = coin.current.toLowerCase();
|
||||
const typeRequest = "feerequired";
|
||||
|
||||
try {
|
||||
const response = await qortalRequestWithTimeout(
|
||||
{
|
||||
action: "GET_FOREIGN_FEE",
|
||||
coin: coin,
|
||||
type: typeRequest,
|
||||
},
|
||||
1800000
|
||||
);
|
||||
if (response && !isNaN(+response)) {
|
||||
setFee(response);
|
||||
}
|
||||
|
||||
console.log("response", response);
|
||||
} catch (error) {
|
||||
setFee("");
|
||||
console.error(error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
establishUpdateFeeForm(coin);
|
||||
console.log("editFee or fetch changed");
|
||||
}, [coin, establishUpdateFeeForm]);
|
||||
|
||||
const recommendedFeeData = useMemo(() => {
|
||||
if (!resource?.data) return null;
|
||||
if (
|
||||
!resource?.data?.["BTC"] ||
|
||||
!resource?.data?.["LTC"] ||
|
||||
!resource?.data?.["DOGE"]
|
||||
)
|
||||
return null;
|
||||
return resource.data;
|
||||
}, [resource?.data]);
|
||||
|
||||
const recommendedFeeDisplay = useMemo(() => {
|
||||
if (!selectedCoin || !recommendedFeeData) return;
|
||||
const coin = getCoinLabel(selectedCoin)?.toUpperCase();
|
||||
return recommendedFeeData[coin][recommendedFee];
|
||||
}, [recommendedFeeData, recommendedFee, selectedCoin]);
|
||||
|
||||
const updateFee = async () => {
|
||||
const typeRequest = "feerequired";
|
||||
|
||||
try {
|
||||
let feeToSave = editFee
|
||||
if(recommendedFee !== 'custom'){
|
||||
feeToSave = calculateFeeFromRate(recommendedFeeDisplay, 300)
|
||||
}
|
||||
console.log('feeToSave', feeToSave)
|
||||
const response = await qortalRequestWithTimeout(
|
||||
{
|
||||
action: "UPDATE_FOREIGN_FEE",
|
||||
coin: coin,
|
||||
type: typeRequest,
|
||||
value: feeToSave,
|
||||
},
|
||||
1800000
|
||||
);
|
||||
|
||||
if (response && !isNaN(+response)) {
|
||||
setFee(response);
|
||||
setOpenAlert(true);
|
||||
setInfo({
|
||||
type: "success",
|
||||
message: "Fee updated!",
|
||||
});
|
||||
setOpenModal(false);
|
||||
} else throw new Error("Unable to update fee");
|
||||
} catch (error) {
|
||||
setOpenAlert(true);
|
||||
setInfo({
|
||||
type: "error",
|
||||
message: error?.message || "Unable to update fee",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (
|
||||
event: React.MouseEvent<HTMLElement>,
|
||||
newAlignment: string
|
||||
) => {
|
||||
if(newAlignment){
|
||||
setRecommendedFee(newAlignment);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const getLatestFees = useCallback(async () => {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`http://devnet-nodes.qortal.link:11112/arbitrary/resources/searchsimple?service=JSON&identifier=foreign-fee&name=Foreign-Fee-Publisher&prefix=true&limit=1&reverse=true`
|
||||
);
|
||||
const data = await res.json();
|
||||
if (data && data?.length > 0) {
|
||||
setFeeLocation(data[0]);
|
||||
}
|
||||
} catch (error) {}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getLatestFees();
|
||||
}, [getLatestFees]);
|
||||
|
||||
if (!fee) return;
|
||||
return (
|
||||
<>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setOpenModal(true);
|
||||
setEditFee(fee);
|
||||
}}
|
||||
sx={{
|
||||
minHeight: "42px",
|
||||
border: "1px solid gray",
|
||||
color: "white",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "5px 5px",
|
||||
gap: "10px",
|
||||
borderRadius: "5px",
|
||||
"&:hover": {
|
||||
border: "1px solid white", // Border color on hover
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography>Fee: {fee}</Typography>
|
||||
|
||||
<ChangeCircleIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
</ButtonBase>
|
||||
{openModal && (
|
||||
<ReusableModal
|
||||
onClickClose={() => {
|
||||
setOpenModal(false);
|
||||
setEditFee(fee);
|
||||
}}
|
||||
backdrop
|
||||
>
|
||||
<CoinActionContainer sx={{
|
||||
width: '450px',
|
||||
maxWidth: '95vw'
|
||||
}}>
|
||||
<CoinActionRow>
|
||||
<HeaderRow>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
Update unlocking fee for {selectedCoin}
|
||||
</Typography>
|
||||
</HeaderRow>
|
||||
</CoinActionRow>
|
||||
<CoinActionRow>
|
||||
<HeaderRow>
|
||||
<Box sx={{
|
||||
width: '100%'
|
||||
}}>
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<CustomLabel htmlFor="standard-adornment-name">
|
||||
Recommended fee selection
|
||||
</CustomLabel>
|
||||
|
||||
<Spacer height="10px" />
|
||||
<ToggleButtonGroup
|
||||
color="primary"
|
||||
value={recommendedFee}
|
||||
exclusive
|
||||
onChange={handleChange}
|
||||
aria-label="Platform"
|
||||
>
|
||||
<ToggleButton value="l">Low</ToggleButton>
|
||||
<ToggleButton value="m">Medium</ToggleButton>
|
||||
<ToggleButton value="h">High</ToggleButton>
|
||||
<ToggleButton value="custom">Custom</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Box>
|
||||
{recommendedFeeDisplay && (
|
||||
<>
|
||||
<Spacer height="15px" />
|
||||
<Typography
|
||||
sx={{
|
||||
color: "white",
|
||||
fontSize: "18px",
|
||||
}}
|
||||
>
|
||||
New fee:{" "}
|
||||
{calculateFeeFromRate(recommendedFeeDisplay, 300)}
|
||||
</Typography>
|
||||
<Spacer height="10px" />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<QuestionMarkIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
This recommended fee is derived from{" "}
|
||||
{recommendedFeeDisplay} per kb, for a transaction that
|
||||
is approximately 300 kB in size.
|
||||
</Typography>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</HeaderRow>
|
||||
</CoinActionRow>
|
||||
{recommendedFee === "custom" && (
|
||||
<CoinActionRow>
|
||||
<HeaderRow>
|
||||
<Box>
|
||||
<CustomLabel htmlFor="standard-adornment-name">
|
||||
Custom fee
|
||||
</CustomLabel>
|
||||
<Spacer height="5px" />
|
||||
<CustomInput
|
||||
id="standard-adornment-name"
|
||||
type="number"
|
||||
value={editFee}
|
||||
onChange={(e) => setEditFee(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</Box>
|
||||
</HeaderRow>
|
||||
</CoinActionRow>
|
||||
)}
|
||||
|
||||
<ButtonBase
|
||||
onClick={updateFee}
|
||||
disabled={(recommendedFee === 'custom' && !editFee)}
|
||||
sx={{
|
||||
minHeight: "42px",
|
||||
border: "1px solid gray",
|
||||
color: "white",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "5px 20px",
|
||||
gap: "10px",
|
||||
borderRadius: "5px",
|
||||
"&:hover": {
|
||||
border: "1px solid white", // Border color on hover
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ChangeCircleIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
<Typography>Update fee</Typography>
|
||||
</ButtonBase>
|
||||
</CoinActionContainer>
|
||||
</ReusableModal>
|
||||
)}
|
||||
<Snackbar
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
|
||||
open={openAlert}
|
||||
onClose={handleCloseAlert}
|
||||
autoHideDuration={6000}
|
||||
>
|
||||
<Alert
|
||||
onClose={handleCloseAlert}
|
||||
severity={info?.type}
|
||||
variant="filled"
|
||||
sx={{ width: "100%" }}
|
||||
>
|
||||
{info?.message}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
</>
|
||||
);
|
||||
};
|
245
src/components/sell/UnsignedFees.tsx
Normal file
245
src/components/sell/UnsignedFees.tsx
Normal file
@ -0,0 +1,245 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { baseLocalHost } from "../Grids/TradeOffers";
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
ButtonBase,
|
||||
Snackbar,
|
||||
SnackbarCloseReason,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import TouchAppIcon from "@mui/icons-material/TouchApp";
|
||||
import { ReusableModal } from "../common/reusable-modal/ReusableModal";
|
||||
import {
|
||||
CoinActionContainer,
|
||||
CoinActionRow,
|
||||
CoinConfirmSendBtn,
|
||||
HeaderRow,
|
||||
} from "../header/Header-styles";
|
||||
|
||||
export default function UnsignedFees({ qortAddress }) {
|
||||
const [isPositive, setIsPositive] = useState(null);
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [openAlert, setOpenAlert] = useState(false);
|
||||
const [info, setInfo] = useState<any>(null);
|
||||
console.log("isPositive", isPositive);
|
||||
const qortAddressRef = useRef(null);
|
||||
|
||||
const handleCloseAlert = (
|
||||
event?: React.SyntheticEvent | Event,
|
||||
reason?: SnackbarCloseReason
|
||||
) => {
|
||||
if (reason === "clickaway") {
|
||||
return;
|
||||
}
|
||||
|
||||
setOpenAlert(false);
|
||||
setInfo(null);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (qortAddress) {
|
||||
qortAddressRef.current = qortAddress;
|
||||
}
|
||||
}, [qortAddress]);
|
||||
|
||||
const restartUnsignedFeeSocket = () => {
|
||||
setTimeout(() => initUnsignedFeeSocket(true), 50);
|
||||
};
|
||||
|
||||
const restartFeeData = () => {
|
||||
if (socketRef.current) {
|
||||
socketRef.current.close(1000, "forced"); // Close with a custom reason
|
||||
socketRef.current = null;
|
||||
}
|
||||
|
||||
setIsPositive(null);
|
||||
};
|
||||
|
||||
const socketRef = useRef(null);
|
||||
|
||||
const initUnsignedFeeSocket = (restarted = false) => {
|
||||
let socketTimeout: any;
|
||||
const socketLink = `${
|
||||
window.location.protocol === "https:" ? "wss:" : "ws:"
|
||||
}//${baseLocalHost}/websockets/crosschain/unsignedfees`;
|
||||
socketRef.current = new WebSocket(socketLink);
|
||||
socketRef.current.onopen = () => {
|
||||
setTimeout(pingSocket, 50);
|
||||
};
|
||||
socketRef.current.onmessage = (e) => {
|
||||
restarted = false;
|
||||
const data = JSON.parse(e.data);
|
||||
if (qortAddressRef.current === data.address) {
|
||||
if (data.positive) {
|
||||
setIsPositive(true);
|
||||
setOpenModal(true);
|
||||
} else {
|
||||
setIsPositive(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
socketRef.current.onclose = (event) => {
|
||||
clearTimeout(socketTimeout);
|
||||
if (event.reason === "forced") {
|
||||
return;
|
||||
}
|
||||
restartUnsignedFeeSocket();
|
||||
};
|
||||
socketRef.current.onerror = (e) => {
|
||||
clearTimeout(socketTimeout);
|
||||
};
|
||||
const pingSocket = () => {
|
||||
socketRef.current.send("ping");
|
||||
socketTimeout = setTimeout(pingSocket, 295000);
|
||||
};
|
||||
};
|
||||
|
||||
const getUnsignedFees = useCallback(async (address)=> {
|
||||
try {
|
||||
const url = `http://devnet-nodes.qortal.link:11112/crosschain/unsignedfees/${address}`
|
||||
const res = await fetch(url)
|
||||
const data = await res.json()
|
||||
if(data && data.length > 0){
|
||||
setIsPositive(true)
|
||||
} else {
|
||||
setIsPositive(false)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!qortAddress) return;
|
||||
restartFeeData();
|
||||
getUnsignedFees(qortAddress)
|
||||
setTimeout(() => {
|
||||
initUnsignedFeeSocket();
|
||||
}, 500);
|
||||
return () => {
|
||||
if (socketRef.current) {
|
||||
socketRef.current.close(1000, "forced");
|
||||
}
|
||||
};
|
||||
}, [qortAddress, getUnsignedFees]);
|
||||
|
||||
const signForeignFees = async () => {
|
||||
try {
|
||||
await qortalRequest({
|
||||
action: "SIGN_FOREIGN_FEES",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
if (!isPositive) return null;
|
||||
return (
|
||||
<>
|
||||
<ButtonBase
|
||||
onClick={signForeignFees}
|
||||
sx={{
|
||||
minHeight: "42px",
|
||||
background: "red",
|
||||
color: "white",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "5px 20px",
|
||||
gap: "20px",
|
||||
borderRadius: "5px",
|
||||
"&:hover": {
|
||||
background: "darkred", // Border color on hover
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TouchAppIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
<Typography>Action required! You have unsigned fees</Typography>
|
||||
</ButtonBase>
|
||||
{openModal && (
|
||||
<ReusableModal
|
||||
onClickClose={() => {
|
||||
setOpenModal(false);
|
||||
}}
|
||||
backdrop
|
||||
>
|
||||
<CoinActionContainer>
|
||||
<CoinActionRow>
|
||||
<HeaderRow>
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
Action required!
|
||||
</Typography>
|
||||
</HeaderRow>
|
||||
</CoinActionRow>
|
||||
<CoinActionRow>
|
||||
<HeaderRow>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
In order for buyers to use the correct unlocking fee, please
|
||||
sign your fees.
|
||||
</Typography>
|
||||
</HeaderRow>
|
||||
</CoinActionRow>
|
||||
<ButtonBase
|
||||
onClick={signForeignFees}
|
||||
sx={{
|
||||
minHeight: "42px",
|
||||
background: "red",
|
||||
color: "white",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "5px 20px",
|
||||
gap: "20px",
|
||||
borderRadius: "5px",
|
||||
"&:hover": {
|
||||
background: "darkred", // Border color on hover
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TouchAppIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
<Typography>Sign Fees</Typography>
|
||||
</ButtonBase>
|
||||
</CoinActionContainer>
|
||||
</ReusableModal>
|
||||
)}
|
||||
<Snackbar
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
|
||||
open={openAlert}
|
||||
onClose={handleCloseAlert}
|
||||
autoHideDuration={6000}
|
||||
>
|
||||
<Alert
|
||||
onClose={handleCloseAlert}
|
||||
severity={info?.type}
|
||||
variant="filled"
|
||||
sx={{ width: "100%" }}
|
||||
>
|
||||
{info?.message}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './App.tsx'
|
||||
import "./index.scss";
|
||||
import { IndexedDBProvider } from './contexts/indexedDBContext.tsx';
|
||||
@ -11,7 +11,7 @@ interface CustomWindow extends Window {
|
||||
const customWindow = window as unknown as CustomWindow;
|
||||
|
||||
const baseUrl = customWindow?._qdnBase || "";
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<BrowserRouter basename={baseUrl}>
|
||||
<IndexedDBProvider>
|
||||
<App />
|
||||
|
@ -24,6 +24,8 @@ export const HomePage = () => {
|
||||
} = useContext(gameContext);
|
||||
const { setNotification } = useContext(NotificationContext);
|
||||
const [mode, setMode] = useState("buy");
|
||||
const [fee, setFee] = useState("");
|
||||
|
||||
const filteredOngoingTrades = useMemo(() => {
|
||||
return onGoingTrades?.filter(
|
||||
(item) => item?.tradeInfo?.foreignBlockchain === selectedCoin
|
||||
@ -50,6 +52,9 @@ export const HomePage = () => {
|
||||
<Header
|
||||
qortBalance={qortBalance}
|
||||
foreignCoinBalance={foreignCoinBalance}
|
||||
qortAddress={userInfo?.address}
|
||||
fee={fee}
|
||||
setFee={setFee}
|
||||
/>
|
||||
|
||||
<AppContainer>
|
||||
@ -108,7 +113,8 @@ export const HomePage = () => {
|
||||
</TextTableTitle>
|
||||
</Box>
|
||||
<Spacer height="10px" />
|
||||
<TradeOffers foreignCoinBalance={foreignCoinBalance} />
|
||||
<TradeOffers fee={fee}
|
||||
setFee={setFee} foreignCoinBalance={foreignCoinBalance} />
|
||||
</div>
|
||||
|
||||
<CreateSell show={mode === "sell"} qortAddress={userInfo?.address} />
|
||||
|
@ -1,42 +0,0 @@
|
||||
import { io, Socket } from "socket.io-client";
|
||||
import { DefaultEventsMap } from '@socket.io/component-emitter';
|
||||
|
||||
class SocketService {
|
||||
public socket: Socket | null = null;
|
||||
|
||||
public connect(
|
||||
url: string,
|
||||
token: string
|
||||
): Promise<Socket<DefaultEventsMap, DefaultEventsMap>> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket = io(url, {
|
||||
auth: {
|
||||
token: token
|
||||
}
|
||||
});
|
||||
|
||||
if (!this.socket) return reject("Socket initialization failed");
|
||||
|
||||
this.socket.on("connect", () => {
|
||||
console.log('connected');
|
||||
resolve(this.socket as Socket);
|
||||
});
|
||||
|
||||
this.socket.on("connect_error", (err) => {
|
||||
console.log("Connection error: ", err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public disconnect() {
|
||||
if (this.socket) {
|
||||
this.socket.disconnect();
|
||||
this.socket = null; // Optionally reset the socket to null after disconnecting
|
||||
console.log('Disconnected');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new SocketService();
|
||||
|
Loading…
x
Reference in New Issue
Block a user