Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c9c4e7d7d1 | |||
| d97bc27c14 | |||
| e215c61ea1 | |||
| 8298d0bc66 | |||
| fac72b1299 | |||
| e780b971d9 | |||
| 90f9f91ddb | |||
| 01838dccd1 | |||
| 42b7f080b0 | |||
| a88e78e748 | |||
| 665ae063fa | |||
| dc04e26db4 | |||
| b798fa0e68 | |||
| 4e564dd5c0 | |||
| 1c53364fcd | |||
| 848e61a7a0 | |||
| c3a940c8d7 | |||
| 02469046b0 | |||
| 1ce4a47495 | |||
| 9a8520eb6f | |||
| ec8fdf0315 | |||
| a4b8e7ca25 | |||
| 693609810d | |||
| 73f845a73a | |||
| b6419297f4 |
@@ -61,6 +61,7 @@ jobs:
|
|||||||
uses: ethersphere/update-supported-bee-action@v1
|
uses: ethersphere/update-supported-bee-action@v1
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
with:
|
with:
|
||||||
|
updateEngine: true
|
||||||
token: ${{ secrets.GHA_PAT_BASIC }}
|
token: ${{ secrets.GHA_PAT_BASIC }}
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -71,6 +72,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create preview
|
- name: Create preview
|
||||||
uses: ethersphere/swarm-actions/pr-preview@v0
|
uses: ethersphere/swarm-actions/pr-preview@v0
|
||||||
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
bee-url: https://unlimited.gateway.ethswarm.org
|
bee-url: https://unlimited.gateway.ethswarm.org
|
||||||
token: ${{ secrets.GHA_PAT_BASIC }}
|
token: ${{ secrets.GHA_PAT_BASIC }}
|
||||||
|
|||||||
@@ -1,5 +1,48 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.23.0](https://github.com/ethersphere/bee-dashboard/compare/v0.22.0...v0.23.0) (2023-02-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add staking for full nodes ([#590](https://github.com/ethersphere/bee-dashboard/issues/590)) ([fac72b1](https://github.com/ethersphere/bee-dashboard/commit/fac72b1299353c104231aa038c1bab9df78c1355))
|
||||||
|
* upgrade bee-js to 5.2.0 ([#611](https://github.com/ethersphere/bee-dashboard/issues/611)) ([e215c61](https://github.com/ethersphere/bee-dashboard/commit/e215c61ea1619fc388fe8b1904d160b04a1a5c0d))
|
||||||
|
|
||||||
|
## [0.22.0](https://github.com/ethersphere/bee-dashboard/compare/v0.21.1...v0.22.0) (2023-01-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add node connecting status ([#603](https://github.com/ethersphere/bee-dashboard/issues/603)) ([90f9f91](https://github.com/ethersphere/bee-dashboard/commit/90f9f91ddbefb47b40c7e567125972b800d81972))
|
||||||
|
|
||||||
|
## [0.21.1](https://github.com/ethersphere/bee-dashboard/compare/v0.21.0...v0.21.1) (2022-12-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* do not require chequebook funding ([#599](https://github.com/ethersphere/bee-dashboard/issues/599)) ([42b7f08](https://github.com/ethersphere/bee-dashboard/commit/42b7f080b00a94f068d2fad4779d02ddcf58e27d))
|
||||||
|
|
||||||
|
## [0.21.0](https://github.com/ethersphere/bee-dashboard/compare/v0.20.2...v0.21.0) (2022-12-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add prerequisite checks before swap ([#588](https://github.com/ethersphere/bee-dashboard/issues/588)) ([4e564dd](https://github.com/ethersphere/bee-dashboard/commit/4e564dd5c08b938c95f07818bc60957a7df4f5bb))
|
||||||
|
* add starting state to sidebar indicator ([#587](https://github.com/ethersphere/bee-dashboard/issues/587)) ([848e61a](https://github.com/ethersphere/bee-dashboard/commit/848e61a7a0fc9b31cae4f603473b37d467f9e914))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add loading state to info page ([#584](https://github.com/ethersphere/bee-dashboard/issues/584)) ([0246904](https://github.com/ethersphere/bee-dashboard/commit/02469046b05512d6617d8b21ca93b41d6a8a6827))
|
||||||
|
* always consider user input when performing swap ([#572](https://github.com/ethersphere/bee-dashboard/issues/572)) ([ec8fdf0](https://github.com/ethersphere/bee-dashboard/commit/ec8fdf0315ed7ee75c7612780c602cba49a2321d))
|
||||||
|
* always set rpc to newly provided value in desktop ([#591](https://github.com/ethersphere/bee-dashboard/issues/591)) ([b798fa0](https://github.com/ethersphere/bee-dashboard/commit/b798fa0e68b367fe324ef64507b1405b642da6e0))
|
||||||
|
* change status page depending on desktop mode ([#573](https://github.com/ethersphere/bee-dashboard/issues/573)) ([a4b8e7c](https://github.com/ethersphere/bee-dashboard/commit/a4b8e7ca2596028e7c8192c92202c0361610e307))
|
||||||
|
* change version mismatch to a warning ([#594](https://github.com/ethersphere/bee-dashboard/issues/594)) ([dc04e26](https://github.com/ethersphere/bee-dashboard/commit/dc04e26db4fe6beb9e76fad79c732794b0b7f77d))
|
||||||
|
* fix conditional rendering for blockchain network ([#583](https://github.com/ethersphere/bee-dashboard/issues/583)) ([1ce4a47](https://github.com/ethersphere/bee-dashboard/commit/1ce4a474954a5ba4debee53b40bb66a46fb19ffc))
|
||||||
|
* handle auth and server error during swap ([#593](https://github.com/ethersphere/bee-dashboard/issues/593)) ([665ae06](https://github.com/ethersphere/bee-dashboard/commit/665ae063fa49bc94762ea10a9098b57e95327d9c))
|
||||||
|
* hide swap in standalone mode ([#582](https://github.com/ethersphere/bee-dashboard/issues/582)) ([9a8520e](https://github.com/ethersphere/bee-dashboard/commit/9a8520eb6fe9f40a77c4230ab79d3731ebdd4b42))
|
||||||
|
* refresh after chequebook withdraw deposit ([#576](https://github.com/ethersphere/bee-dashboard/issues/576)) ([6936098](https://github.com/ethersphere/bee-dashboard/commit/693609810d735d1e54691b13ea0e4db33e678a53))
|
||||||
|
|
||||||
## [0.20.2](https://github.com/ethersphere/bee-dashboard/compare/v0.20.1...v0.20.2) (2022-09-15)
|
## [0.20.2](https://github.com/ethersphere/bee-dashboard/compare/v0.20.1...v0.20.2) (2022-09-15)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
**Warning: This project is in alpha state. There might (and most probably will) be changes in the future to its API and
|
**Warning: This project is in alpha state. There might (and most probably will) be changes in the future to its API and
|
||||||
working. Also, no guarantees can be made about its stability, efficiency, and security at this stage.**
|
working. Also, no guarantees can be made about its stability, efficiency, and security at this stage.**
|
||||||
|
|
||||||
This project is intended to be used with **Bee version <!-- SUPPORTED_BEE_START -->1.7.0-bbf13011<!-- SUPPORTED_BEE_END -->**.
|
This project is intended to be used with **Bee version <!-- SUPPORTED_BEE_START -->1.12.0-88c1d236<!-- SUPPORTED_BEE_END -->**.
|
||||||
Using it with older or newer Bee versions is not recommended and may not work. Stay up to date by joining the
|
Using it with older or newer Bee versions is not recommended and may not work. Stay up to date by joining the
|
||||||
[official Discord](https://discord.gg/GU22h2utj6) and by keeping an eye on the
|
[official Discord](https://discord.gg/GU22h2utj6) and by keeping an eye on the
|
||||||
[releases tab](https://github.com/ethersphere/bee-dashboard/releases).
|
[releases tab](https://github.com/ethersphere/bee-dashboard/releases).
|
||||||
@@ -107,10 +107,10 @@ We support following variables:
|
|||||||
|
|
||||||
#### Swarm Desktop development
|
#### Swarm Desktop development
|
||||||
|
|
||||||
If you want to develop Bee Dashboard in the Swarm Desktop mode, then spin up `swarm-desktop` to the point where you see Bee Dashboard (eq. install Bee etc.) and:
|
If you want to develop Bee Dashboard in the Swarm Desktop mode, then spin up `swarm-desktop` to the point where Desktop is initialized (eq. the splash screen disappear) and:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
echo "REACT_APP_BEE_DESKTOP_URL=http://localhost:3000
|
echo "REACT_APP_BEE_DESKTOP_URL=http://localhost:3054
|
||||||
REACT_APP_BEE_DESKTOP_ENABLED=true" > .env.development.local
|
REACT_APP_BEE_DESKTOP_ENABLED=true" > .env.development.local
|
||||||
|
|
||||||
npm start
|
npm start
|
||||||
|
|||||||
Generated
+168
-62
@@ -1,15 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "@ethersphere/bee-dashboard",
|
"name": "@ethersphere/bee-dashboard",
|
||||||
"version": "0.20.2",
|
"version": "0.23.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@ethersphere/bee-dashboard",
|
"name": "@ethersphere/bee-dashboard",
|
||||||
"version": "0.20.2",
|
"version": "0.23.0",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersphere/bee-js": "^5.0.0",
|
"@ethersphere/bee-js": "^5.2.0",
|
||||||
"@ethersphere/manifest-js": "1.2.1",
|
"@ethersphere/manifest-js": "1.2.1",
|
||||||
"@ethersphere/swarm-cid": "^0.1.0",
|
"@ethersphere/swarm-cid": "^0.1.0",
|
||||||
"@material-ui/core": "4.12.3",
|
"@material-ui/core": "4.12.3",
|
||||||
@@ -31,9 +31,9 @@
|
|||||||
"notistack": "1.0.10",
|
"notistack": "1.0.10",
|
||||||
"opener": "1.5.2",
|
"opener": "1.5.2",
|
||||||
"qrcode.react": "1.0.1",
|
"qrcode.react": "1.0.1",
|
||||||
"react": ">=17.0.0 || >=18.0.0",
|
"react": ">= 17.0.2",
|
||||||
"react-copy-to-clipboard": "5.0.4",
|
"react-copy-to-clipboard": "5.0.4",
|
||||||
"react-dom": ">=17.0.0 || >=18.0.0",
|
"react-dom": ">= 17.0.2",
|
||||||
"react-identicons": "1.2.5",
|
"react-identicons": "1.2.5",
|
||||||
"react-router": "6.2.1",
|
"react-router": "6.2.1",
|
||||||
"react-router-dom": "6.2.1",
|
"react-router-dom": "6.2.1",
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
"webpack-cli": "^4.10.0"
|
"webpack-cli": "^4.10.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"bee": ">=0.6.0",
|
"bee": "1.11.1-1992b846",
|
||||||
"node": ">=14.0.0",
|
"node": ">=14.0.0",
|
||||||
"npm": ">=6.9.0"
|
"npm": ">=6.9.0"
|
||||||
},
|
},
|
||||||
@@ -2440,9 +2440,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ethersphere/bee-js": {
|
"node_modules/@ethersphere/bee-js": {
|
||||||
"version": "5.0.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-5.2.0.tgz",
|
||||||
"integrity": "sha512-Dr5Xon0UZvi37fvbyGj46kw3/0D8fydwfDtVtFHKi2p7mNEizG0uok2mXvwLZrMvUMOS8uXkFhbQjTFBjB+pWA==",
|
"integrity": "sha512-BM9lBiKC/tMbYNTEs3uyDyAwpED+v6u46DEXyFh7BLCUPg12LBWv0O12XmQj5ybzXnUT4ptHU9UXu3oGHph8Vw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersphere/swarm-cid": "^0.1.0",
|
"@ethersphere/swarm-cid": "^0.1.0",
|
||||||
"@types/readable-stream": "^2.3.13",
|
"@types/readable-stream": "^2.3.13",
|
||||||
@@ -2451,18 +2451,18 @@
|
|||||||
"fetch-blob": "2.1.2",
|
"fetch-blob": "2.1.2",
|
||||||
"isomorphic-ws": "^4.0.1",
|
"isomorphic-ws": "^4.0.1",
|
||||||
"js-sha3": "^0.8.0",
|
"js-sha3": "^0.8.0",
|
||||||
"ky": "^0.25.1",
|
"ky": "^0.33.2",
|
||||||
"ky-universal": "^0.8.2",
|
"ky-universal": "^0.11.0",
|
||||||
"semver": "^7.3.5",
|
"semver": "^7.3.5",
|
||||||
"tar-js": "^0.3.0",
|
"tar-js": "^0.3.0",
|
||||||
"utf-8-validate": "^5.0.9",
|
"utf-8-validate": "^5.0.9",
|
||||||
"web-streams-polyfill": "^4.0.0-beta.1",
|
"web-streams-polyfill": "^4.0.0-beta.3",
|
||||||
"ws": "^8.7.0"
|
"ws": "^8.7.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"bee": "1.7.0-bbf13011",
|
"bee": "1.12.0-88c1d236",
|
||||||
"beeApiVersion": "3.0.2",
|
"beeApiVersion": "4.0.0",
|
||||||
"beeDebugApiVersion": "3.0.2",
|
"beeDebugApiVersion": "4.0.0",
|
||||||
"node": ">=14.0.0",
|
"node": ">=14.0.0",
|
||||||
"npm": ">=6.0.0"
|
"npm": ">=6.0.0"
|
||||||
}
|
}
|
||||||
@@ -8212,11 +8212,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/data-uri-to-buffer": {
|
"node_modules/data-uri-to-buffer": {
|
||||||
"version": "3.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||||
"integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==",
|
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/data-urls": {
|
"node_modules/data-urls": {
|
||||||
@@ -10664,6 +10664,39 @@
|
|||||||
"node": ">=0.4.x"
|
"node": ">=0.4.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/formdata-polyfill": {
|
||||||
|
"version": "4.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||||
|
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||||
|
"dependencies": {
|
||||||
|
"fetch-blob": "^3.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/formdata-polyfill/node_modules/fetch-blob": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/jimmywarting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "paypal",
|
||||||
|
"url": "https://paypal.me/jimmywarting"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"node-domexception": "^1.0.0",
|
||||||
|
"web-streams-polyfill": "^3.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20 || >= 14.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/formik": {
|
"node_modules/formik": {
|
||||||
"version": "2.2.9",
|
"version": "2.2.9",
|
||||||
"resolved": "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz",
|
"resolved": "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz",
|
||||||
@@ -13694,33 +13727,33 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ky": {
|
"node_modules/ky": {
|
||||||
"version": "0.25.1",
|
"version": "0.33.2",
|
||||||
"resolved": "https://registry.npmjs.org/ky/-/ky-0.25.1.tgz",
|
"resolved": "https://registry.npmjs.org/ky/-/ky-0.33.2.tgz",
|
||||||
"integrity": "sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA==",
|
"integrity": "sha512-f6oS2rKUcPu5FzdqCDbFpmzis/JlqFZw8uIHm/jf8Kc3vtnW+VDhuashOAKyBZv8bFiZFZUMNxTC0JtahEvujA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=14.16"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sindresorhus/ky?sponsor=1"
|
"url": "https://github.com/sindresorhus/ky?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ky-universal": {
|
"node_modules/ky-universal": {
|
||||||
"version": "0.8.2",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.11.0.tgz",
|
||||||
"integrity": "sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==",
|
"integrity": "sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"abort-controller": "^3.0.0",
|
"abort-controller": "^3.0.0",
|
||||||
"node-fetch": "3.0.0-beta.9"
|
"node-fetch": "^3.2.10"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.17"
|
"node": ">=14.16"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sindresorhus/ky-universal?sponsor=1"
|
"url": "https://github.com/sindresorhus/ky-universal?sponsor=1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"ky": ">=0.17.0",
|
"ky": ">=0.31.4",
|
||||||
"web-streams-polyfill": ">=2.0.0"
|
"web-streams-polyfill": ">=3.2.1"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"web-streams-polyfill": {
|
"web-streams-polyfill": {
|
||||||
@@ -14364,22 +14397,63 @@
|
|||||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/node-domexception": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/jimmywarting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://paypal.me/jimmywarting"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "3.0.0-beta.9",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0-beta.9.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz",
|
||||||
"integrity": "sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==",
|
"integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"data-uri-to-buffer": "^3.0.1",
|
"data-uri-to-buffer": "^4.0.0",
|
||||||
"fetch-blob": "^2.1.1"
|
"fetch-blob": "^3.1.4",
|
||||||
|
"formdata-polyfill": "^4.0.10"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10.17 || >=12.3"
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/node-fetch"
|
"url": "https://opencollective.com/node-fetch"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-fetch/node_modules/fetch-blob": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/jimmywarting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "paypal",
|
||||||
|
"url": "https://paypal.me/jimmywarting"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"node-domexception": "^1.0.0",
|
||||||
|
"web-streams-polyfill": "^3.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20 || >= 14.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-forge": {
|
"node_modules/node-forge": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||||
@@ -19738,8 +19812,6 @@
|
|||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
|
||||||
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
|
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
@@ -22383,9 +22455,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ethersphere/bee-js": {
|
"@ethersphere/bee-js": {
|
||||||
"version": "5.0.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-5.2.0.tgz",
|
||||||
"integrity": "sha512-Dr5Xon0UZvi37fvbyGj46kw3/0D8fydwfDtVtFHKi2p7mNEizG0uok2mXvwLZrMvUMOS8uXkFhbQjTFBjB+pWA==",
|
"integrity": "sha512-BM9lBiKC/tMbYNTEs3uyDyAwpED+v6u46DEXyFh7BLCUPg12LBWv0O12XmQj5ybzXnUT4ptHU9UXu3oGHph8Vw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@ethersphere/swarm-cid": "^0.1.0",
|
"@ethersphere/swarm-cid": "^0.1.0",
|
||||||
"@types/readable-stream": "^2.3.13",
|
"@types/readable-stream": "^2.3.13",
|
||||||
@@ -22394,12 +22466,12 @@
|
|||||||
"fetch-blob": "2.1.2",
|
"fetch-blob": "2.1.2",
|
||||||
"isomorphic-ws": "^4.0.1",
|
"isomorphic-ws": "^4.0.1",
|
||||||
"js-sha3": "^0.8.0",
|
"js-sha3": "^0.8.0",
|
||||||
"ky": "^0.25.1",
|
"ky": "^0.33.2",
|
||||||
"ky-universal": "^0.8.2",
|
"ky-universal": "^0.11.0",
|
||||||
"semver": "^7.3.5",
|
"semver": "^7.3.5",
|
||||||
"tar-js": "^0.3.0",
|
"tar-js": "^0.3.0",
|
||||||
"utf-8-validate": "^5.0.9",
|
"utf-8-validate": "^5.0.9",
|
||||||
"web-streams-polyfill": "^4.0.0-beta.1",
|
"web-streams-polyfill": "^4.0.0-beta.3",
|
||||||
"ws": "^8.7.0"
|
"ws": "^8.7.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -26625,9 +26697,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"data-uri-to-buffer": {
|
"data-uri-to-buffer": {
|
||||||
"version": "3.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||||
"integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og=="
|
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="
|
||||||
},
|
},
|
||||||
"data-urls": {
|
"data-urls": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@@ -28485,6 +28557,25 @@
|
|||||||
"resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
|
||||||
"integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs="
|
"integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs="
|
||||||
},
|
},
|
||||||
|
"formdata-polyfill": {
|
||||||
|
"version": "4.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||||
|
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||||
|
"requires": {
|
||||||
|
"fetch-blob": "^3.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"fetch-blob": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
||||||
|
"requires": {
|
||||||
|
"node-domexception": "^1.0.0",
|
||||||
|
"web-streams-polyfill": "^3.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"formik": {
|
"formik": {
|
||||||
"version": "2.2.9",
|
"version": "2.2.9",
|
||||||
"resolved": "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz",
|
"resolved": "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz",
|
||||||
@@ -30735,17 +30826,17 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ky": {
|
"ky": {
|
||||||
"version": "0.25.1",
|
"version": "0.33.2",
|
||||||
"resolved": "https://registry.npmjs.org/ky/-/ky-0.25.1.tgz",
|
"resolved": "https://registry.npmjs.org/ky/-/ky-0.33.2.tgz",
|
||||||
"integrity": "sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA=="
|
"integrity": "sha512-f6oS2rKUcPu5FzdqCDbFpmzis/JlqFZw8uIHm/jf8Kc3vtnW+VDhuashOAKyBZv8bFiZFZUMNxTC0JtahEvujA=="
|
||||||
},
|
},
|
||||||
"ky-universal": {
|
"ky-universal": {
|
||||||
"version": "0.8.2",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.11.0.tgz",
|
||||||
"integrity": "sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==",
|
"integrity": "sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"abort-controller": "^3.0.0",
|
"abort-controller": "^3.0.0",
|
||||||
"node-fetch": "3.0.0-beta.9"
|
"node-fetch": "^3.2.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"language-subtag-registry": {
|
"language-subtag-registry": {
|
||||||
@@ -31260,13 +31351,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node-domexception": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
|
||||||
|
},
|
||||||
"node-fetch": {
|
"node-fetch": {
|
||||||
"version": "3.0.0-beta.9",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0-beta.9.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz",
|
||||||
"integrity": "sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==",
|
"integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"data-uri-to-buffer": "^3.0.1",
|
"data-uri-to-buffer": "^4.0.0",
|
||||||
"fetch-blob": "^2.1.1"
|
"fetch-blob": "^3.1.4",
|
||||||
|
"formdata-polyfill": "^4.0.10"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"fetch-blob": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
||||||
|
"requires": {
|
||||||
|
"node-domexception": "^1.0.0",
|
||||||
|
"web-streams-polyfill": "^3.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node-forge": {
|
"node-forge": {
|
||||||
@@ -35166,9 +35274,7 @@
|
|||||||
"web-streams-polyfill": {
|
"web-streams-polyfill": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
|
||||||
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
|
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q=="
|
||||||
"optional": true,
|
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"web-vitals": {
|
"web-vitals": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
|
|||||||
+7
-6
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ethersphere/bee-dashboard",
|
"name": "@ethersphere/bee-dashboard",
|
||||||
"version": "0.20.2",
|
"version": "0.23.0",
|
||||||
"description": "An app which helps users to setup their Bee node and do actions like cash out cheques",
|
"description": "An app which helps users to setup their Bee node and do actions like cash out cheques",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"bee",
|
"bee",
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"url": "https://github.com/ethersphere/bee-dashboard.git"
|
"url": "https://github.com/ethersphere/bee-dashboard.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersphere/bee-js": "^5.0.0",
|
"@ethersphere/bee-js": "^5.2.0",
|
||||||
"@ethersphere/manifest-js": "1.2.1",
|
"@ethersphere/manifest-js": "1.2.1",
|
||||||
"@ethersphere/swarm-cid": "^0.1.0",
|
"@ethersphere/swarm-cid": "^0.1.0",
|
||||||
"@material-ui/core": "4.12.3",
|
"@material-ui/core": "4.12.3",
|
||||||
@@ -48,9 +48,9 @@
|
|||||||
"notistack": "1.0.10",
|
"notistack": "1.0.10",
|
||||||
"opener": "1.5.2",
|
"opener": "1.5.2",
|
||||||
"qrcode.react": "1.0.1",
|
"qrcode.react": "1.0.1",
|
||||||
"react": ">=17.0.0 || >=18.0.0",
|
"react": ">= 17.0.2",
|
||||||
"react-copy-to-clipboard": "5.0.4",
|
"react-copy-to-clipboard": "5.0.4",
|
||||||
"react-dom": ">=17.0.0 || >=18.0.0",
|
"react-dom": ">= 17.0.2",
|
||||||
"react-identicons": "1.2.5",
|
"react-identicons": "1.2.5",
|
||||||
"react-router": "6.2.1",
|
"react-router": "6.2.1",
|
||||||
"react-router-dom": "6.2.1",
|
"react-router-dom": "6.2.1",
|
||||||
@@ -136,7 +136,8 @@
|
|||||||
"lint": "eslint --fix \"src/**/*.ts\" \"src/**/*.tsx\" && prettier --write \"src/**/*.ts\" \"src/**/*.tsx\"",
|
"lint": "eslint --fix \"src/**/*.ts\" \"src/**/*.tsx\" && prettier --write \"src/**/*.ts\" \"src/**/*.tsx\"",
|
||||||
"lint:check": "eslint \"src/**/*.ts\" \"src/**/*.tsx\" && prettier --check \"src/**/*.ts\" \"src/**/*.tsx\"",
|
"lint:check": "eslint \"src/**/*.ts\" \"src/**/*.tsx\" && prettier --check \"src/**/*.ts\" \"src/**/*.tsx\"",
|
||||||
"check:types": "tsc --project tsconfig.lib.json",
|
"check:types": "tsc --project tsconfig.lib.json",
|
||||||
"update-map-data": "node ./utils/update-map-data.js"
|
"update-map-data": "node ./utils/update-map-data.js",
|
||||||
|
"bee": "bee-factory start"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib",
|
"lib",
|
||||||
@@ -158,6 +159,6 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0",
|
"node": ">=14.0.0",
|
||||||
"npm": ">=6.9.0",
|
"npm": ">=6.9.0",
|
||||||
"bee": ">=0.6.0"
|
"bee": "1.12.0-88c1d236"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||||
import { ThemeProvider } from '@material-ui/core/styles'
|
import { ThemeProvider } from '@material-ui/core/styles'
|
||||||
import { SnackbarProvider } from 'notistack'
|
import { SnackbarProvider } from 'notistack'
|
||||||
import React, { ReactElement } from 'react'
|
import { ReactElement } from 'react'
|
||||||
import { HashRouter as Router } from 'react-router-dom'
|
import { HashRouter as Router } from 'react-router-dom'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
import Dashboard from './layout/Dashboard'
|
import Dashboard from './layout/Dashboard'
|
||||||
@@ -47,7 +47,7 @@ const App = ({
|
|||||||
desktopUrl={desktopUrl}
|
desktopUrl={desktopUrl}
|
||||||
>
|
>
|
||||||
<TopUpProvider>
|
<TopUpProvider>
|
||||||
<BeeProvider>
|
<BeeProvider isDesktop={isDesktop}>
|
||||||
<BalanceProvider>
|
<BalanceProvider>
|
||||||
<StampsProvider>
|
<StampsProvider>
|
||||||
<FileProvider>
|
<FileProvider>
|
||||||
|
|||||||
+16
-2
@@ -2,6 +2,8 @@ import { createStyles, makeStyles, Theme, Typography } from '@material-ui/core'
|
|||||||
import { ReactElement } from 'react'
|
import { ReactElement } from 'react'
|
||||||
import Check from 'remixicon-react/CheckLineIcon'
|
import Check from 'remixicon-react/CheckLineIcon'
|
||||||
import AlertCircle from 'remixicon-react/ErrorWarningFillIcon'
|
import AlertCircle from 'remixicon-react/ErrorWarningFillIcon'
|
||||||
|
import Connecting from 'remixicon-react/LinksLineIcon'
|
||||||
|
import RefreshLine from 'remixicon-react/RefreshLineIcon'
|
||||||
import { SwarmButton, SwarmButtonProps } from './SwarmButton'
|
import { SwarmButton, SwarmButtonProps } from './SwarmButton'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -9,7 +11,7 @@ interface Props {
|
|||||||
title: string
|
title: string
|
||||||
subtitle: string
|
subtitle: string
|
||||||
buttonProps: SwarmButtonProps
|
buttonProps: SwarmButtonProps
|
||||||
status: 'ok' | 'error'
|
status: 'ok' | 'error' | 'loading' | 'connecting'
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = (backgroundColor: string) =>
|
const useStyles = (backgroundColor: string) =>
|
||||||
@@ -56,12 +58,24 @@ export default function Card({ buttonProps, icon, title, subtitle, status }: Pro
|
|||||||
const { className, ...rest } = buttonProps
|
const { className, ...rest } = buttonProps
|
||||||
const classes = useStyles(backgroundColor)()
|
const classes = useStyles(backgroundColor)()
|
||||||
|
|
||||||
|
let statusIcon = null
|
||||||
|
|
||||||
|
if (status === 'ok') {
|
||||||
|
statusIcon = <Check size="13" color="#09ca6c" />
|
||||||
|
} else if (status === 'error') {
|
||||||
|
statusIcon = <AlertCircle size="13" color="#f44336" />
|
||||||
|
} else if (status === 'loading') {
|
||||||
|
statusIcon = <RefreshLine size="13" color="orange" />
|
||||||
|
} else if (status === 'connecting') {
|
||||||
|
statusIcon = <Connecting size="13" color="#0074D9" />
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<div className={classes.wrapper}>
|
<div className={classes.wrapper}>
|
||||||
<div className={classes.iconWrapper}>
|
<div className={classes.iconWrapper}>
|
||||||
{icon}
|
{icon}
|
||||||
{status === 'ok' ? <Check size="13" color="#09ca6c" /> : <AlertCircle size="13" color="#f44336" />}
|
{statusIcon}
|
||||||
</div>
|
</div>
|
||||||
<Typography variant="h2" style={{ marginBottom: '8px' }}>
|
<Typography variant="h2" style={{ marginBottom: '8px' }}>
|
||||||
{title}
|
{title}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import { useLocation, matchPath } from 'react-router-dom'
|
import { matchPath, useLocation } from 'react-router-dom'
|
||||||
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
|
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
|
||||||
|
|
||||||
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'
|
import { ListItem, ListItemIcon, ListItemText, Typography } from '@material-ui/core'
|
||||||
import { ListItemText, ListItemIcon, ListItem, Typography } from '@material-ui/core'
|
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
|
||||||
import { Context } from '../providers/Bee'
|
import { Context } from '../providers/Bee'
|
||||||
import StatusIcon from './StatusIcon'
|
import StatusIcon from './StatusIcon'
|
||||||
|
|
||||||
@@ -44,6 +44,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
},
|
},
|
||||||
smallerText: {
|
smallerText: {
|
||||||
fontSize: '0.9rem',
|
fontSize: '0.9rem',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { ReactElement } from 'react'
|
|
||||||
import { CircularProgress } from '@material-ui/core'
|
import { CircularProgress } from '@material-ui/core'
|
||||||
|
import type { ReactElement } from 'react'
|
||||||
import { CheckState } from '../providers/Bee'
|
import { CheckState } from '../providers/Bee'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -25,6 +25,12 @@ export default function StatusIcon({ checkState, size, className, isLoading }: P
|
|||||||
case CheckState.ERROR:
|
case CheckState.ERROR:
|
||||||
backgroundColor = '#ff3a52'
|
backgroundColor = '#ff3a52'
|
||||||
break
|
break
|
||||||
|
case CheckState.STARTING:
|
||||||
|
backgroundColor = 'orange'
|
||||||
|
break
|
||||||
|
case CheckState.CONNECTING:
|
||||||
|
backgroundColor = '#0074D9'
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
// Default is error
|
// Default is error
|
||||||
backgroundColor = '#ff3a52'
|
backgroundColor = '#ff3a52'
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
import { BigNumber } from 'bignumber.js'
|
||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import Download from 'remixicon-react/DownloadLineIcon'
|
import Download from 'remixicon-react/DownloadLineIcon'
|
||||||
import { Context as SettingsContext } from '../providers/Settings'
|
|
||||||
|
|
||||||
import WithdrawDepositModal from '../components/WithdrawDepositModal'
|
import WithdrawDepositModal from '../components/WithdrawDepositModal'
|
||||||
import { BigNumber } from 'bignumber.js'
|
import { Context as BeeContext } from '../providers/Bee'
|
||||||
|
import { Context as SettingsContext } from '../providers/Settings'
|
||||||
|
|
||||||
export default function DepositModal(): ReactElement {
|
export default function DepositModal(): ReactElement {
|
||||||
const { beeDebugApi } = useContext(SettingsContext)
|
const { beeDebugApi } = useContext(SettingsContext)
|
||||||
|
const { refresh } = useContext(BeeContext)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WithdrawDepositModal
|
<WithdrawDepositModal
|
||||||
@@ -16,10 +17,13 @@ export default function DepositModal(): ReactElement {
|
|||||||
label="Deposit"
|
label="Deposit"
|
||||||
icon={<Download size="1rem" />}
|
icon={<Download size="1rem" />}
|
||||||
min={new BigNumber(0)}
|
min={new BigNumber(0)}
|
||||||
action={(amount: bigint) => {
|
action={async (amount: bigint) => {
|
||||||
if (!beeDebugApi) throw new Error('Bee Debug URL is not valid')
|
if (!beeDebugApi) throw new Error('Bee Debug URL is not valid')
|
||||||
|
|
||||||
return beeDebugApi.depositTokens(amount.toString())
|
const transactionHash = await beeDebugApi.depositTokens(amount.toString())
|
||||||
|
refresh()
|
||||||
|
|
||||||
|
return transactionHash
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { BigNumber } from 'bignumber.js'
|
||||||
|
import { ReactElement, useContext } from 'react'
|
||||||
|
import Download from 'remixicon-react/DownloadLineIcon'
|
||||||
|
import WithdrawDepositModal from '../components/WithdrawDepositModal'
|
||||||
|
import { Context as BeeContext } from '../providers/Bee'
|
||||||
|
import { Context as SettingsContext } from '../providers/Settings'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onStarted: () => void
|
||||||
|
onFinished: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function StakeModal({ onStarted, onFinished }: Props): ReactElement {
|
||||||
|
const { beeDebugApi } = useContext(SettingsContext)
|
||||||
|
const { refresh } = useContext(BeeContext)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WithdrawDepositModal
|
||||||
|
successMessage="Successfully deposited stake."
|
||||||
|
errorMessage="Error with depositing"
|
||||||
|
dialogMessage="Specify the amount of xBZZ you would like to stake. Your first stake must be at least 10 xBZZ. This will lock your tokens."
|
||||||
|
label="Stake"
|
||||||
|
icon={<Download size="1rem" />}
|
||||||
|
min={new BigNumber(0)}
|
||||||
|
action={async (amount: bigint) => {
|
||||||
|
if (!beeDebugApi) throw new Error('Bee Debug URL is not valid')
|
||||||
|
|
||||||
|
onStarted()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await beeDebugApi.depositStake(amount.toString())
|
||||||
|
} finally {
|
||||||
|
refresh()
|
||||||
|
onFinished()
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,10 +2,12 @@ import { BigNumber } from 'bignumber.js'
|
|||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import Upload from 'remixicon-react/UploadLineIcon'
|
import Upload from 'remixicon-react/UploadLineIcon'
|
||||||
import WithdrawDepositModal from '../components/WithdrawDepositModal'
|
import WithdrawDepositModal from '../components/WithdrawDepositModal'
|
||||||
|
import { Context as BeeContext } from '../providers/Bee'
|
||||||
import { Context as SettingsContext } from '../providers/Settings'
|
import { Context as SettingsContext } from '../providers/Settings'
|
||||||
|
|
||||||
export default function WithdrawModal(): ReactElement {
|
export default function WithdrawModal(): ReactElement {
|
||||||
const { beeDebugApi } = useContext(SettingsContext)
|
const { beeDebugApi } = useContext(SettingsContext)
|
||||||
|
const { refresh } = useContext(BeeContext)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WithdrawDepositModal
|
<WithdrawDepositModal
|
||||||
@@ -15,10 +17,13 @@ export default function WithdrawModal(): ReactElement {
|
|||||||
label="Withdraw"
|
label="Withdraw"
|
||||||
icon={<Upload size="1rem" />}
|
icon={<Upload size="1rem" />}
|
||||||
min={new BigNumber(0)}
|
min={new BigNumber(0)}
|
||||||
action={(amount: bigint) => {
|
action={async (amount: bigint) => {
|
||||||
if (!beeDebugApi) throw new Error('Bee Debug URL is not valid')
|
if (!beeDebugApi) throw new Error('Bee Debug URL is not valid')
|
||||||
|
|
||||||
return beeDebugApi.withdrawTokens(amount.toString())
|
const transactionHash = await beeDebugApi.withdrawTokens(amount.toString())
|
||||||
|
refresh()
|
||||||
|
|
||||||
|
return transactionHash
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
+27
-20
@@ -1,8 +1,7 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { getLatestBeeDesktopVersion } from '../utils/desktop'
|
|
||||||
import { getJson } from '../utils/net'
|
|
||||||
import { GITHUB_REPO_URL } from '../constants'
|
import { GITHUB_REPO_URL } from '../constants'
|
||||||
|
import { BeeConfig, getDesktopConfiguration, getLatestBeeDesktopVersion } from '../utils/desktop'
|
||||||
|
|
||||||
export interface LatestBeeReleaseHook {
|
export interface LatestBeeReleaseHook {
|
||||||
latestBeeRelease: LatestBeeRelease | null
|
latestBeeRelease: LatestBeeRelease | null
|
||||||
@@ -11,6 +10,7 @@ export interface LatestBeeReleaseHook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface BeeDesktopHook {
|
export interface BeeDesktopHook {
|
||||||
|
reachable: boolean
|
||||||
error: Error | null
|
error: Error | null
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
beeDesktopVersion: string
|
beeDesktopVersion: string
|
||||||
@@ -22,11 +22,34 @@ export interface NewDesktopVersionHook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useBeeDesktop = (isBeeDesktop = false, desktopUrl: string): BeeDesktopHook => {
|
export const useBeeDesktop = (isBeeDesktop = false, desktopUrl: string): BeeDesktopHook => {
|
||||||
|
const [reachable, setReachable] = useState(false)
|
||||||
const [desktopAutoUpdateEnabled, setDesktopAutoUpdateEnabled] = useState<boolean>(true)
|
const [desktopAutoUpdateEnabled, setDesktopAutoUpdateEnabled] = useState<boolean>(true)
|
||||||
const [beeDesktopVersion, setBeeDesktopVersion] = useState<string>('')
|
const [beeDesktopVersion, setBeeDesktopVersion] = useState<string>('')
|
||||||
const [isLoading, setLoading] = useState<boolean>(true)
|
const [isLoading, setLoading] = useState<boolean>(true)
|
||||||
const [error, setError] = useState<Error | null>(null)
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isBeeDesktop) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function runReachabilityCheck() {
|
||||||
|
axios
|
||||||
|
.get(`${desktopUrl}/info`)
|
||||||
|
.then(() => {
|
||||||
|
setReachable(true)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setReachable(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
runReachabilityCheck()
|
||||||
|
const interval = setInterval(runReachabilityCheck, 10_000)
|
||||||
|
|
||||||
|
return () => clearInterval(interval)
|
||||||
|
}, [desktopUrl, isBeeDesktop])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isBeeDesktop) {
|
if (!isBeeDesktop) {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
@@ -48,7 +71,7 @@ export const useBeeDesktop = (isBeeDesktop = false, desktopUrl: string): BeeDesk
|
|||||||
}
|
}
|
||||||
}, [desktopUrl, isBeeDesktop])
|
}, [desktopUrl, isBeeDesktop])
|
||||||
|
|
||||||
return { error, isLoading, beeDesktopVersion, desktopAutoUpdateEnabled }
|
return { error, isLoading, beeDesktopVersion, desktopAutoUpdateEnabled, reachable }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkNewVersion(desktopUrl: string): Promise<string> {
|
async function checkNewVersion(desktopUrl: string): Promise<string> {
|
||||||
@@ -85,22 +108,6 @@ export function useNewBeeDesktopVersion(
|
|||||||
return { newBeeDesktopVersion }
|
return { newBeeDesktopVersion }
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BeeConfig {
|
|
||||||
'api-addr': string
|
|
||||||
'debug-api-addr': string
|
|
||||||
'debug-api-enable': boolean
|
|
||||||
password: string
|
|
||||||
'swap-enable': boolean
|
|
||||||
'swap-initial-deposit': bigint
|
|
||||||
mainnet: boolean
|
|
||||||
'full-node': boolean
|
|
||||||
'cors-allowed-origins': string
|
|
||||||
'resolver-options': string
|
|
||||||
'use-postage-snapshot': boolean
|
|
||||||
'data-dir': string
|
|
||||||
'swap-endpoint'?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetBeeConfig {
|
export interface GetBeeConfig {
|
||||||
config: BeeConfig | null
|
config: BeeConfig | null
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
@@ -113,7 +120,7 @@ export const useGetBeeConfig = (desktopUrl: string): GetBeeConfig => {
|
|||||||
const [error, setError] = useState<Error | null>(null)
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getJson<BeeConfig>(`${desktopUrl}/config`)
|
getDesktopConfiguration(desktopUrl)
|
||||||
.then(beeConf => {
|
.then(beeConf => {
|
||||||
setBeeConfig(beeConf)
|
setBeeConfig(beeConf)
|
||||||
setError(null)
|
setError(null)
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import { BigNumber } from 'bignumber.js'
|
import { BigNumber } from 'bignumber.js'
|
||||||
import { Token } from './Token'
|
import { Token } from './Token'
|
||||||
|
|
||||||
|
export const BZZ_DECIMAL_PLACES = 16
|
||||||
|
|
||||||
export class BzzToken extends Token {
|
export class BzzToken extends Token {
|
||||||
constructor(amount: BigNumber | string | bigint) {
|
constructor(value: BigNumber | string | bigint) {
|
||||||
super(amount, 16)
|
super(value, BZZ_DECIMAL_PLACES)
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromDecimal(value: BigNumber | string | bigint): BzzToken {
|
||||||
|
return Token.fromDecimal(value, BZZ_DECIMAL_PLACES)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import { BigNumber } from 'bignumber.js'
|
import { BigNumber } from 'bignumber.js'
|
||||||
import { Token } from './Token'
|
import { Token } from './Token'
|
||||||
|
|
||||||
|
const DAI_DECIMAL_PLACES = 18
|
||||||
|
|
||||||
export class DaiToken extends Token {
|
export class DaiToken extends Token {
|
||||||
constructor(amount: BigNumber | string | bigint) {
|
constructor(value: BigNumber | string | bigint) {
|
||||||
super(amount, 18)
|
super(value, DAI_DECIMAL_PLACES)
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromDecimal(value: BigNumber | string | bigint): DaiToken {
|
||||||
|
return Token.fromDecimal(value, DAI_DECIMAL_PLACES)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,4 +87,11 @@ export class Token {
|
|||||||
this.decimals,
|
this.decimals,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plusBaseUnits(amount: string): Token {
|
||||||
|
return new Token(
|
||||||
|
this.toBigNumber.plus(new BigNumber(amount).multipliedBy(new BigNumber(10).pow(this.decimals))),
|
||||||
|
this.decimals,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
import { BeeModes } from '@ethersphere/bee-js'
|
||||||
import { createStyles, makeStyles, Tab, Tabs, Theme } from '@material-ui/core'
|
import { createStyles, makeStyles, Tab, Tabs, Theme } from '@material-ui/core'
|
||||||
import { ReactElement } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import { Context } from '../../providers/Bee'
|
||||||
import { ACCOUNT_TABS } from '../../routes'
|
import { ACCOUNT_TABS } from '../../routes'
|
||||||
|
|
||||||
const tabMap = {
|
const tabMap = {
|
||||||
@@ -8,10 +10,11 @@ const tabMap = {
|
|||||||
CHEQUEBOOK: 1,
|
CHEQUEBOOK: 1,
|
||||||
STAMPS: 2,
|
STAMPS: 2,
|
||||||
FEEDS: 3,
|
FEEDS: 3,
|
||||||
|
STAKING: 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
active: 'WALLET' | 'CHEQUEBOOK' | 'STAMPS' | 'FEEDS'
|
active: 'WALLET' | 'CHEQUEBOOK' | 'STAMPS' | 'FEEDS' | 'STAKING'
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
@@ -20,16 +23,12 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
marginBottom: theme.spacing(4),
|
marginBottom: theme.spacing(4),
|
||||||
textTransform: 'none',
|
textTransform: 'none',
|
||||||
|
marginLeft: theme.spacing(-0.25),
|
||||||
|
marginRight: theme.spacing(-0.25),
|
||||||
},
|
},
|
||||||
leftTab: {
|
tab: {
|
||||||
marginRight: theme.spacing(0.125),
|
marginLeft: theme.spacing(0.25),
|
||||||
},
|
marginRight: theme.spacing(0.25),
|
||||||
centerTab: {
|
|
||||||
marginLeft: theme.spacing(0.125),
|
|
||||||
marginRight: theme.spacing(0.125),
|
|
||||||
},
|
|
||||||
rightTab: {
|
|
||||||
marginLeft: theme.spacing(0.125),
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -37,6 +36,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
export function AccountNavigation({ active }: Props): ReactElement {
|
export function AccountNavigation({ active }: Props): ReactElement {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const { nodeInfo } = useContext(Context)
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<Record<string, never>>, newValue: number) {
|
function onChange(event: React.ChangeEvent<Record<string, never>>, newValue: number) {
|
||||||
navigate(ACCOUNT_TABS[newValue])
|
navigate(ACCOUNT_TABS[newValue])
|
||||||
@@ -45,10 +45,11 @@ export function AccountNavigation({ active }: Props): ReactElement {
|
|||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<Tabs value={tabMap[active]} onChange={onChange} variant="fullWidth">
|
<Tabs value={tabMap[active]} onChange={onChange} variant="fullWidth">
|
||||||
<Tab className={classes.leftTab} key="WALLET" label="Wallet" />
|
<Tab className={classes.tab} key="WALLET" label="Wallet" />
|
||||||
<Tab className={classes.centerTab} key="CHEQUEBOOK" label="Chequebook" />
|
<Tab className={classes.tab} key="CHEQUEBOOK" label="Chequebook" />
|
||||||
<Tab className={classes.centerTab} key="STAMPS" label="Stamps" />
|
<Tab className={classes.tab} key="STAMPS" label="Stamps" />
|
||||||
<Tab className={classes.rightTab} key="FEEDS" label="Feeds" />
|
<Tab className={classes.tab} key="FEEDS" label="Feeds" />
|
||||||
|
{nodeInfo?.beeMode === BeeModes.FULL ? <Tab className={classes.tab} key="STAKING" label="Staking" /> : null}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
|
import ExpandableList from '../../../components/ExpandableList'
|
||||||
|
import ExpandableListItem from '../../../components/ExpandableListItem'
|
||||||
|
import ExpandableListItemActions from '../../../components/ExpandableListItemActions'
|
||||||
|
import { Loading } from '../../../components/Loading'
|
||||||
|
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
|
||||||
|
import StakeModal from '../../../containers/StakeModal'
|
||||||
|
import { CheckState, Context as BeeContext } from '../../../providers/Bee'
|
||||||
|
import { Context as BalanceContext } from '../../../providers/WalletBalance'
|
||||||
|
import { AccountNavigation } from '../AccountNavigation'
|
||||||
|
import { Header } from '../Header'
|
||||||
|
|
||||||
|
export function AccountStaking(): ReactElement {
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
const { status, stake } = useContext(BeeContext)
|
||||||
|
const { balance } = useContext(BalanceContext)
|
||||||
|
|
||||||
|
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
|
||||||
|
|
||||||
|
function onStarted() {
|
||||||
|
setLoading(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFinished() {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
|
<AccountNavigation active="STAKING" />
|
||||||
|
<div>
|
||||||
|
{loading || stake?.toDecimal === undefined ? (
|
||||||
|
<Loading />
|
||||||
|
) : (
|
||||||
|
<ExpandableList label="Staking" defaultOpen>
|
||||||
|
<ExpandableListItem label="Staked BZZ" value={`${stake?.toSignificantDigits()} xBZZ`} />
|
||||||
|
{balance?.bzz ? (
|
||||||
|
<ExpandableListItem
|
||||||
|
label="Available xBZZ balance"
|
||||||
|
value={`${balance?.bzz.toSignificantDigits(4)} xBZZ`}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<ExpandableListItemActions>
|
||||||
|
<StakeModal onStarted={onStarted} onFinished={onFinished} />
|
||||||
|
</ExpandableListItemActions>
|
||||||
|
</ExpandableList>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -46,9 +46,11 @@ export function AccountWallet(): ReactElement {
|
|||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<Grid container direction="row" justifyContent="space-between" alignItems="center">
|
<Grid container direction="row" justifyContent="space-between" alignItems="center">
|
||||||
<Typography variant="h2">Wallet balance</Typography>
|
<Typography variant="h2">Wallet balance</Typography>
|
||||||
<SwarmButton onClick={onDeposit} iconType={Download}>
|
{isDesktop && (
|
||||||
Top up wallet
|
<SwarmButton onClick={onDeposit} iconType={Download}>
|
||||||
</SwarmButton>
|
Top up wallet
|
||||||
|
</SwarmButton>
|
||||||
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
{balance && nodeAddresses ? (
|
{balance && nodeAddresses ? (
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { useContext } from 'react'
|
||||||
|
import { useNavigate } from 'react-router'
|
||||||
|
import ExchangeFunds from 'remixicon-react/ExchangeFundsLineIcon'
|
||||||
|
import Card from '../../components/Card'
|
||||||
|
import { Context as BeeContext } from '../../providers/Bee'
|
||||||
|
import { ROUTES } from '../../routes'
|
||||||
|
|
||||||
|
export function ChequebookInfoCard() {
|
||||||
|
const { chequebookBalance } = useContext(BeeContext)
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
if (
|
||||||
|
chequebookBalance?.availableBalance !== undefined &&
|
||||||
|
chequebookBalance?.availableBalance.toBigNumber.isGreaterThan(0)
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
buttonProps={{
|
||||||
|
iconType: ExchangeFunds,
|
||||||
|
children: 'View chequebook',
|
||||||
|
onClick: () => navigate(ROUTES.ACCOUNT_CHEQUEBOOK),
|
||||||
|
}}
|
||||||
|
icon={<ExchangeFunds />}
|
||||||
|
title={`${chequebookBalance?.availableBalance.toSignificantDigits(4)} xBZZ`}
|
||||||
|
subtitle="Current chequebook balance."
|
||||||
|
status="ok"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
buttonProps={{
|
||||||
|
iconType: ExchangeFunds,
|
||||||
|
children: 'View chequebook',
|
||||||
|
onClick: () => navigate(ROUTES.ACCOUNT_CHEQUEBOOK),
|
||||||
|
}}
|
||||||
|
icon={<ExchangeFunds />}
|
||||||
|
title={
|
||||||
|
chequebookBalance?.availableBalance
|
||||||
|
? `${chequebookBalance.availableBalance.toSignificantDigits(4)} xBZZ`
|
||||||
|
: 'No available balance.'
|
||||||
|
}
|
||||||
|
subtitle="Chequebook not setup."
|
||||||
|
status="error"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,17 +1,41 @@
|
|||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import Search from 'remixicon-react/SearchLineIcon'
|
|
||||||
import Globe from 'remixicon-react/GlobalLineIcon'
|
import Globe from 'remixicon-react/GlobalLineIcon'
|
||||||
|
import Search from 'remixicon-react/SearchLineIcon'
|
||||||
import Settings from 'remixicon-react/Settings2LineIcon'
|
import Settings from 'remixicon-react/Settings2LineIcon'
|
||||||
|
|
||||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
|
||||||
import Card from '../../components/Card'
|
|
||||||
import { useNavigate } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
|
import Card from '../../components/Card'
|
||||||
|
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
|
|
||||||
export default function NodeInfoCard(): ReactElement {
|
export default function NodeInfoCard(): ReactElement {
|
||||||
const { status } = useContext(BeeContext)
|
const { status } = useContext(BeeContext)
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
if (status.all === CheckState.CONNECTING) {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
buttonProps={{ iconType: Settings, children: 'Open node setup', onClick: () => navigate(ROUTES.STATUS) }}
|
||||||
|
icon={<Globe />}
|
||||||
|
title="Connecting..."
|
||||||
|
subtitle="Attempting to establish connection to your Bee node."
|
||||||
|
status="connecting"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.all === CheckState.STARTING) {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
buttonProps={{ iconType: Settings, children: 'Open node setup', onClick: () => navigate(ROUTES.STATUS) }}
|
||||||
|
icon={<Globe />}
|
||||||
|
title="Starting up..."
|
||||||
|
subtitle="Your Bee node is currently launching."
|
||||||
|
status="loading"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (status.all === CheckState.ERROR) {
|
if (status.all === CheckState.ERROR) {
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { useContext } from 'react'
|
||||||
|
import { useNavigate } from 'react-router'
|
||||||
|
import Upload from 'remixicon-react/UploadLineIcon'
|
||||||
|
import Wallet from 'remixicon-react/Wallet3LineIcon'
|
||||||
|
import Card from '../../components/Card'
|
||||||
|
import { Context as BeeContext } from '../../providers/Bee'
|
||||||
|
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||||
|
import { ROUTES } from '../../routes'
|
||||||
|
|
||||||
|
export function WalletInfoCard() {
|
||||||
|
const { nodeInfo } = useContext(BeeContext)
|
||||||
|
const { balance, error } = useContext(BalanceProvider)
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
let balanceText = 'Loading...'
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
balanceText = 'Could not load...'
|
||||||
|
console.error(error) // eslint-disable-line
|
||||||
|
} else if (balance) {
|
||||||
|
balanceText = `${balance.bzz.toSignificantDigits(4)} xBZZ | ${balance.dai.toSignificantDigits(4)} xDAI`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeInfo?.beeMode && ['light', 'full', 'dev'].includes(nodeInfo.beeMode)) {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
buttonProps={{
|
||||||
|
iconType: Wallet,
|
||||||
|
children: 'Manage your wallet',
|
||||||
|
onClick: () => navigate(ROUTES.ACCOUNT_WALLET),
|
||||||
|
}}
|
||||||
|
icon={<Wallet />}
|
||||||
|
title={balanceText}
|
||||||
|
subtitle="Current wallet balance."
|
||||||
|
status="ok"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
buttonProps={{
|
||||||
|
iconType: Wallet,
|
||||||
|
children: 'Setup wallet',
|
||||||
|
onClick: () => navigate(ROUTES.TOP_UP),
|
||||||
|
}}
|
||||||
|
icon={<Upload />}
|
||||||
|
title="Your wallet is not setup."
|
||||||
|
subtitle="To share content on Swarm, please setup your wallet."
|
||||||
|
status="error"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
+10
-79
@@ -1,110 +1,41 @@
|
|||||||
import { Button } from '@material-ui/core'
|
import { Button } from '@material-ui/core'
|
||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import { useNavigate } from 'react-router'
|
|
||||||
import ExchangeFunds from 'remixicon-react/ExchangeFundsLineIcon'
|
|
||||||
import Upload from 'remixicon-react/UploadLineIcon'
|
|
||||||
import Wallet from 'remixicon-react/Wallet3LineIcon'
|
|
||||||
import Card from '../../components/Card'
|
|
||||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||||
import Map from '../../components/Map'
|
import Map from '../../components/Map'
|
||||||
|
import { BEE_DESKTOP_LATEST_RELEASE_PAGE } from '../../constants'
|
||||||
import { useBeeDesktop, useNewBeeDesktopVersion } from '../../hooks/apiHooks'
|
import { useBeeDesktop, useNewBeeDesktopVersion } from '../../hooks/apiHooks'
|
||||||
import { Context as BeeContext } from '../../providers/Bee'
|
import { Context as BeeContext } from '../../providers/Bee'
|
||||||
import { Context as SettingsContext } from '../../providers/Settings'
|
import { Context as SettingsContext } from '../../providers/Settings'
|
||||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
|
||||||
import { ROUTES } from '../../routes'
|
|
||||||
import { chainIdToName } from '../../utils/chain'
|
import { chainIdToName } from '../../utils/chain'
|
||||||
|
import { ChequebookInfoCard } from './ChequebookInfoCard'
|
||||||
import NodeInfoCard from './NodeInfoCard'
|
import NodeInfoCard from './NodeInfoCard'
|
||||||
import { BEE_DESKTOP_LATEST_RELEASE_PAGE } from '../../constants'
|
import { WalletInfoCard } from './WalletInfoCard'
|
||||||
|
|
||||||
export default function Status(): ReactElement {
|
export default function Status(): ReactElement {
|
||||||
const {
|
const {
|
||||||
|
debugApiReadiness,
|
||||||
status,
|
status,
|
||||||
latestUserVersion,
|
latestUserVersion,
|
||||||
isLatestBeeVersion,
|
isLatestBeeVersion,
|
||||||
latestBeeVersionUrl,
|
latestBeeVersionUrl,
|
||||||
topology,
|
topology,
|
||||||
nodeInfo,
|
nodeInfo,
|
||||||
chequebookBalance,
|
|
||||||
chainId,
|
chainId,
|
||||||
} = useContext(BeeContext)
|
} = useContext(BeeContext)
|
||||||
const { isDesktop, desktopUrl } = useContext(SettingsContext)
|
const { isDesktop, desktopUrl } = useContext(SettingsContext)
|
||||||
const { balance, error } = useContext(BalanceProvider)
|
|
||||||
const { beeDesktopVersion } = useBeeDesktop(isDesktop, desktopUrl)
|
const { beeDesktopVersion } = useBeeDesktop(isDesktop, desktopUrl)
|
||||||
const { newBeeDesktopVersion } = useNewBeeDesktopVersion(isDesktop, desktopUrl, false)
|
const { newBeeDesktopVersion } = useNewBeeDesktopVersion(isDesktop, desktopUrl, false)
|
||||||
const navigate = useNavigate()
|
|
||||||
|
|
||||||
let balanceText = 'Loading...'
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
balanceText = 'Could not load...'
|
|
||||||
console.error(error) // eslint-disable-line
|
|
||||||
} else if (balance) {
|
|
||||||
balanceText = `${balance.bzz.toSignificantDigits(4)} xBZZ | ${balance.dai.toSignificantDigits(4)} xDAI`
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'stretch', alignContent: 'stretch' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'stretch', alignContent: 'stretch' }}>
|
||||||
<NodeInfoCard />
|
<NodeInfoCard />
|
||||||
<div style={{ width: '8px' }}></div>
|
{debugApiReadiness && (
|
||||||
{nodeInfo?.beeMode && ['light', 'full', 'dev'].includes(nodeInfo.beeMode) ? (
|
|
||||||
<Card
|
|
||||||
buttonProps={{
|
|
||||||
iconType: Wallet,
|
|
||||||
children: 'Manage your wallet',
|
|
||||||
onClick: () => navigate(ROUTES.ACCOUNT_WALLET),
|
|
||||||
}}
|
|
||||||
icon={<Wallet />}
|
|
||||||
title={balanceText}
|
|
||||||
subtitle="Current wallet balance."
|
|
||||||
status="ok"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Card
|
|
||||||
buttonProps={{
|
|
||||||
iconType: Wallet,
|
|
||||||
children: 'Setup wallet',
|
|
||||||
onClick: () => navigate(ROUTES.TOP_UP),
|
|
||||||
}}
|
|
||||||
icon={<Upload />}
|
|
||||||
title="Your wallet is not setup."
|
|
||||||
subtitle="To share content on Swarm, please setup your wallet."
|
|
||||||
status="error"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{nodeInfo?.beeMode && ['light', 'full', 'dev'].includes(nodeInfo.beeMode) && (
|
|
||||||
<>
|
<>
|
||||||
<div style={{ width: '8px' }} />
|
<div style={{ width: '8px' }}></div>
|
||||||
{chequebookBalance?.availableBalance !== undefined &&
|
<WalletInfoCard />
|
||||||
chequebookBalance?.availableBalance.toBigNumber.isGreaterThan(0) ? (
|
<div style={{ width: '8px' }}></div>
|
||||||
<Card
|
<ChequebookInfoCard />
|
||||||
buttonProps={{
|
|
||||||
iconType: ExchangeFunds,
|
|
||||||
children: 'View chequebook',
|
|
||||||
onClick: () => navigate(ROUTES.ACCOUNT_CHEQUEBOOK),
|
|
||||||
}}
|
|
||||||
icon={<ExchangeFunds />}
|
|
||||||
title={`${chequebookBalance?.availableBalance.toSignificantDigits(4)} xBZZ`}
|
|
||||||
subtitle="Current chequebook balance."
|
|
||||||
status="ok"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Card
|
|
||||||
buttonProps={{
|
|
||||||
iconType: ExchangeFunds,
|
|
||||||
children: 'View chequebook',
|
|
||||||
onClick: () => navigate(ROUTES.ACCOUNT_CHEQUEBOOK),
|
|
||||||
}}
|
|
||||||
icon={<ExchangeFunds />}
|
|
||||||
title={
|
|
||||||
chequebookBalance?.availableBalance
|
|
||||||
? `${chequebookBalance.availableBalance.toSignificantDigits(4)} xBZZ`
|
|
||||||
: 'No available balance.'
|
|
||||||
}
|
|
||||||
subtitle="Chequebook not setup."
|
|
||||||
status="error"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -158,7 +89,7 @@ export default function Status(): ReactElement {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ExpandableListItem label="Mode" value={nodeInfo?.beeMode} />
|
<ExpandableListItem label="Mode" value={nodeInfo?.beeMode} />
|
||||||
{chainId && <ExpandableListItem label="Blockchain network" value={chainIdToName(chainId)} />}
|
{chainId !== null && <ExpandableListItem label="Blockchain network" value={chainIdToName(chainId)} />}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { BeeModes } from '@ethersphere/bee-js'
|
import { BeeModes } from '@ethersphere/bee-js'
|
||||||
import { Box, Grid, Typography } from '@material-ui/core'
|
import { Box, Grid, Typography } from '@material-ui/core'
|
||||||
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
import { Waiting } from '../../components/Waiting'
|
import { Waiting } from '../../components/Waiting'
|
||||||
@@ -12,6 +13,7 @@ export default function LightModeRestart(): ReactElement {
|
|||||||
const [startedAt] = useState(Number.parseInt(localStorage.getItem(STARTED_UPGRADE_AT) ?? Date.now().toFixed()))
|
const [startedAt] = useState(Number.parseInt(localStorage.getItem(STARTED_UPGRADE_AT) ?? Date.now().toFixed()))
|
||||||
const { apiHealth, nodeInfo } = useContext(Context)
|
const { apiHealth, nodeInfo } = useContext(Context)
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(STARTED_UPGRADE_AT, startedAt.toFixed())
|
localStorage.setItem(STARTED_UPGRADE_AT, startedAt.toFixed())
|
||||||
@@ -23,10 +25,11 @@ export default function LightModeRestart(): ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (apiHealth && nodeInfo?.beeMode === BeeModes.LIGHT) {
|
if (apiHealth && nodeInfo?.beeMode === BeeModes.LIGHT) {
|
||||||
|
enqueueSnackbar('Upgraded to light node', { variant: 'success' })
|
||||||
localStorage.removeItem(STARTED_UPGRADE_AT)
|
localStorage.removeItem(STARTED_UPGRADE_AT)
|
||||||
navigate(ROUTES.INFO)
|
navigate(ROUTES.INFO)
|
||||||
}
|
}
|
||||||
}, [startedAt, navigate, nodeInfo, apiHealth])
|
}, [startedAt, navigate, nodeInfo, apiHealth, enqueueSnackbar])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container direction="column" justifyContent="center" alignItems="center">
|
<Grid container direction="column" justifyContent="center" alignItems="center">
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||||
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import ExpandableList from '../../components/ExpandableList'
|
import ExpandableList from '../../components/ExpandableList'
|
||||||
import ExpandableListItemInput from '../../components/ExpandableListItemInput'
|
import ExpandableListItemInput from '../../components/ExpandableListItemInput'
|
||||||
import { Context as BeeContext } from '../../providers/Bee'
|
import { Context as BeeContext } from '../../providers/Bee'
|
||||||
import { Context as SettingsContext } from '../../providers/Settings'
|
import { Context as SettingsContext } from '../../providers/Settings'
|
||||||
import { useSnackbar } from 'notistack'
|
import { getDesktopConfiguration, restartBeeNode, setJsonRpcInDesktop } from '../../utils/desktop'
|
||||||
import { restartBeeNode, setJsonRpcInDesktop } from '../../utils/desktop'
|
|
||||||
import { BeeModes } from '@ethersphere/bee-js'
|
|
||||||
|
|
||||||
export default function SettingsPage(): ReactElement {
|
export default function SettingsPage(): ReactElement {
|
||||||
const {
|
const {
|
||||||
@@ -24,17 +23,17 @@ export default function SettingsPage(): ReactElement {
|
|||||||
desktopUrl,
|
desktopUrl,
|
||||||
setAndPersistJsonRpcProvider,
|
setAndPersistJsonRpcProvider,
|
||||||
} = useContext(SettingsContext)
|
} = useContext(SettingsContext)
|
||||||
const { refresh, nodeInfo } = useContext(BeeContext)
|
const { refresh } = useContext(BeeContext)
|
||||||
const { enqueueSnackbar, closeSnackbar } = useSnackbar()
|
const { enqueueSnackbar, closeSnackbar } = useSnackbar()
|
||||||
|
|
||||||
async function handleSetRpcUrl(value: string) {
|
async function handleSetRpcUrl(value: string) {
|
||||||
try {
|
try {
|
||||||
setAndPersistJsonRpcProvider(value)
|
setAndPersistJsonRpcProvider(value)
|
||||||
|
|
||||||
// We can't set the RPC URL to the `swap-endpoint` Bee config value unless the Bee node is already in
|
const shouldUpdateDesktop = isDesktop && (await getDesktopConfiguration(desktopUrl))['swap-endpoint']
|
||||||
// light mode as setting this config value, basically upgrades the node to light mode.
|
|
||||||
if (isDesktop && nodeInfo?.beeMode === BeeModes.LIGHT) {
|
if (shouldUpdateDesktop) {
|
||||||
await setJsonRpcInDesktop(desktopUrl, rpcProviderUrl)
|
await setJsonRpcInDesktop(desktopUrl, value)
|
||||||
const snackKey = enqueueSnackbar('RPC endpoint successfully changed, restarting Bee node...', {
|
const snackKey = enqueueSnackbar('RPC endpoint successfully changed, restarting Bee node...', {
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
})
|
})
|
||||||
@@ -48,7 +47,7 @@ export default function SettingsPage(): ReactElement {
|
|||||||
await refresh()
|
await refresh()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e) //eslint-disable-line
|
console.error(e) //eslint-disable-line
|
||||||
enqueueSnackbar(`Failed to change RPC endpoint. Error: ${e}`, { variant: 'error' })
|
enqueueSnackbar(`Failed to change RPC endpoint. ${e}`, { variant: 'error' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,23 +11,21 @@ import { CheckState, Context } from '../../../providers/Bee'
|
|||||||
const ChequebookDeployFund = (): ReactElement | null => {
|
const ChequebookDeployFund = (): ReactElement | null => {
|
||||||
const { status, isLoading, chequebookAddress } = useContext(Context)
|
const { status, isLoading, chequebookAddress } = useContext(Context)
|
||||||
const { checkState, isEnabled } = status.chequebook
|
const { checkState, isEnabled } = status.chequebook
|
||||||
|
const { checkState: debugApiCheckState } = status.debugApiConnection
|
||||||
|
|
||||||
if (!isEnabled) return null
|
if (!isEnabled || debugApiCheckState === CheckState.ERROR) return null
|
||||||
|
|
||||||
let text: ReactNode
|
let text: ReactNode
|
||||||
|
|
||||||
switch (checkState) {
|
switch (checkState) {
|
||||||
case CheckState.OK:
|
case CheckState.OK:
|
||||||
text = 'Your chequebook is deployed and funded'
|
|
||||||
break
|
|
||||||
case CheckState.WARNING:
|
|
||||||
text = (
|
text = (
|
||||||
<>
|
<>
|
||||||
Your chequebook is not funded. Please deposit some xBZZ to your chequebook address. You may need to aquire BZZ
|
Your chequebook is deployed. You may deposit some xBZZ to your chequebook to afford more traffic. You can
|
||||||
(e.g. <a href="https://bzz.exchange/">bzz.exchange</a>) and bridge it to the Gnosis Chain network through the{' '}
|
acquire BZZ (e.g. <a href="https://bzz.exchange/">bzz.exchange</a>) and bridge it to the Gnosis Chain network
|
||||||
<a href="https://omni.gnosischain.com/bridge">omni bridge</a>. To pay the transaction fees, you will also need
|
through the <a href="https://omni.gnosischain.com/bridge">omni bridge</a>. To pay the transaction fees, you
|
||||||
xDAI token. You can purchase DAI on the Ethereum mainnet network and bridge it to Gnosis Chain network through
|
will also need xDAI token. You can purchase DAI on the Ethereum mainnet network and bridge it to Gnosis Chain
|
||||||
the <a href="https://bridge.gnosischain.com">xDai Bridge</a>. See the{' '}
|
network through the <a href="https://bridge.gnosischain.com">xDai Bridge</a>. See the{' '}
|
||||||
<a href="https://www.gnosischain.com">official Gnosis Chain website</a> for more information.
|
<a href="https://www.gnosischain.com">official Gnosis Chain website</a> for more information.
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { Context as SettingsContext } from '../../../providers/Settings'
|
|||||||
|
|
||||||
export default function NodeConnectionCheck(): ReactElement | null {
|
export default function NodeConnectionCheck(): ReactElement | null {
|
||||||
const { status, isLoading } = useContext(Context)
|
const { status, isLoading } = useContext(Context)
|
||||||
const { setDebugApiUrl, apiDebugUrl } = useContext(SettingsContext)
|
const { setDebugApiUrl, apiDebugUrl, isDesktop } = useContext(SettingsContext)
|
||||||
const { checkState, isEnabled } = status.debugApiConnection
|
const { checkState, isEnabled } = status.debugApiConnection
|
||||||
|
|
||||||
if (!isEnabled) return null
|
if (!isEnabled) return null
|
||||||
@@ -26,12 +26,12 @@ export default function NodeConnectionCheck(): ReactElement | null {
|
|||||||
>
|
>
|
||||||
<ExpandableListItemNote>
|
<ExpandableListItemNote>
|
||||||
{checkState === CheckState.OK
|
{checkState === CheckState.OK
|
||||||
? 'The connection to the Bee nodes debug API has been successful'
|
? 'The connection to the Bee node debug API has been successful'
|
||||||
: 'We cannot connect to your nodes debug API. Please check the following to troubleshoot your issue.'}
|
: 'Could not connect to your Bee node debug API.'}
|
||||||
</ExpandableListItemNote>
|
</ExpandableListItemNote>
|
||||||
<ExpandableListItemInput label="Bee Debug API" value={apiDebugUrl} onConfirm={setDebugApiUrl} />
|
<ExpandableListItemInput label="Bee Debug API" value={apiDebugUrl} onConfirm={setDebugApiUrl} />
|
||||||
|
|
||||||
{checkState === CheckState.ERROR && (
|
{checkState === CheckState.ERROR && !isDesktop && (
|
||||||
<ExpandableList level={1} label="Troubleshoot">
|
<ExpandableList level={1} label="Troubleshoot">
|
||||||
<ExpandableListItem
|
<ExpandableListItem
|
||||||
label={
|
label={
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { ReactElement, useContext } from 'react'
|
||||||
|
|
||||||
|
import ExpandableList from '../../../components/ExpandableList'
|
||||||
|
import ExpandableListItem from '../../../components/ExpandableListItem'
|
||||||
|
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
|
||||||
|
import StatusIcon from '../../../components/StatusIcon'
|
||||||
|
import { useBeeDesktop } from '../../../hooks/apiHooks'
|
||||||
|
import { CheckState } from '../../../providers/Bee'
|
||||||
|
import { Context as SettingsContext } from '../../../providers/Settings'
|
||||||
|
|
||||||
|
export default function DesktopConnectionCheck(): ReactElement | null {
|
||||||
|
const { isDesktop, desktopUrl } = useContext(SettingsContext)
|
||||||
|
const { reachable } = useBeeDesktop(isDesktop, desktopUrl)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ExpandableList
|
||||||
|
label={
|
||||||
|
<>
|
||||||
|
<StatusIcon checkState={reachable ? CheckState.OK : CheckState.ERROR} isLoading={false} /> Connection to Swarm
|
||||||
|
Desktop
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ExpandableListItemNote>
|
||||||
|
{reachable
|
||||||
|
? 'The connection to the Swarm Desktop API has been successful'
|
||||||
|
: 'Could not connect to the Swarm Desktop API'}
|
||||||
|
</ExpandableListItemNote>
|
||||||
|
<ExpandableListItem label="Swarm Desktop API" value={desktopUrl} />
|
||||||
|
</ExpandableList>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ import StatusIcon from '../../../components/StatusIcon'
|
|||||||
import { CheckState, Context } from '../../../providers/Bee'
|
import { CheckState, Context } from '../../../providers/Bee'
|
||||||
|
|
||||||
export default function NodeConnectionCheck(): ReactElement | null {
|
export default function NodeConnectionCheck(): ReactElement | null {
|
||||||
const { setApiUrl, apiUrl } = useContext(SettingsContext)
|
const { setApiUrl, apiUrl, isDesktop } = useContext(SettingsContext)
|
||||||
const { status, isLoading } = useContext(Context)
|
const { status, isLoading } = useContext(Context)
|
||||||
const { isEnabled, checkState } = status.apiConnection
|
const { isEnabled, checkState } = status.apiConnection
|
||||||
|
|
||||||
@@ -26,11 +26,11 @@ export default function NodeConnectionCheck(): ReactElement | null {
|
|||||||
>
|
>
|
||||||
<ExpandableListItemNote>
|
<ExpandableListItemNote>
|
||||||
{checkState === CheckState.OK
|
{checkState === CheckState.OK
|
||||||
? 'The connection to the Bee nodes API has been successful'
|
? 'The connection to the Bee node API has been successful'
|
||||||
: 'Could not connect to your Bee nodes API. Please check the troubleshoot below on how you may resolve it.'}
|
: 'Could not connect to your Bee node API.'}
|
||||||
</ExpandableListItemNote>
|
</ExpandableListItemNote>
|
||||||
<ExpandableListItemInput label="Bee API" value={apiUrl} onConfirm={setApiUrl} />
|
<ExpandableListItemInput label="Bee API" value={apiUrl} onConfirm={setApiUrl} />
|
||||||
{checkState === CheckState.ERROR && (
|
{checkState === CheckState.ERROR && !isDesktop && (
|
||||||
<ExpandableList level={1} label="Troubleshoot">
|
<ExpandableList level={1} label="Troubleshoot">
|
||||||
<ExpandableListItem
|
<ExpandableListItem
|
||||||
label={
|
label={
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import { CheckState, Context } from '../../../providers/Bee'
|
|||||||
export default function PeerConnection(): ReactElement | null {
|
export default function PeerConnection(): ReactElement | null {
|
||||||
const { status, isLoading, topology } = useContext(Context)
|
const { status, isLoading, topology } = useContext(Context)
|
||||||
const { isEnabled, checkState } = status.topology
|
const { isEnabled, checkState } = status.topology
|
||||||
|
const { checkState: debugApiCheckState } = status.debugApiConnection
|
||||||
|
|
||||||
if (!isEnabled) return null
|
if (!isEnabled || debugApiCheckState === CheckState.ERROR) return null
|
||||||
|
|
||||||
let text: ReactNode
|
let text: ReactNode
|
||||||
switch (checkState) {
|
switch (checkState) {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import type { ReactElement } from 'react'
|
import { Context } from '../../providers/Settings'
|
||||||
|
import { ReactElement, useContext } from 'react'
|
||||||
|
|
||||||
import DebugConnectionCheck from './SetupSteps/DebugConnectionCheck'
|
import DebugConnectionCheck from './SetupSteps/DebugConnectionCheck'
|
||||||
|
import DesktopConnection from './SetupSteps/DesktopConnectionCheck'
|
||||||
import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck'
|
import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck'
|
||||||
import VersionCheck from './SetupSteps/VersionCheck'
|
import VersionCheck from './SetupSteps/VersionCheck'
|
||||||
import EthereumConnectionCheck from './SetupSteps/EthereumConnectionCheck'
|
import EthereumConnectionCheck from './SetupSteps/EthereumConnectionCheck'
|
||||||
@@ -8,13 +10,16 @@ import ChequebookDeployFund from './SetupSteps/ChequebookDeployFund'
|
|||||||
import PeerConnection from './SetupSteps/PeerConnection'
|
import PeerConnection from './SetupSteps/PeerConnection'
|
||||||
|
|
||||||
export default function NodeSetupWorkflow(): ReactElement {
|
export default function NodeSetupWorkflow(): ReactElement {
|
||||||
|
const { isDesktop } = useContext(Context)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
{isDesktop && <DesktopConnection />}
|
||||||
|
<NodeConnectionCheck />
|
||||||
<DebugConnectionCheck />
|
<DebugConnectionCheck />
|
||||||
<VersionCheck />
|
{!isDesktop && <VersionCheck />}
|
||||||
<EthereumConnectionCheck />
|
<EthereumConnectionCheck />
|
||||||
<ChequebookDeployFund />
|
<ChequebookDeployFund />
|
||||||
<NodeConnectionCheck />
|
|
||||||
<PeerConnection />
|
<PeerConnection />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
import { BeeModes } from '@ethersphere/bee-js'
|
||||||
import { Box, Typography } from '@material-ui/core'
|
import { Box, Typography } from '@material-ui/core'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||||
import Check from 'remixicon-react/CheckLineIcon'
|
|
||||||
import ArrowDown from 'remixicon-react/ArrowDownLineIcon'
|
|
||||||
import { useNavigate, useParams } from 'react-router'
|
import { useNavigate, useParams } from 'react-router'
|
||||||
|
import ArrowDown from 'remixicon-react/ArrowDownLineIcon'
|
||||||
|
import Check from 'remixicon-react/CheckLineIcon'
|
||||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||||
@@ -18,7 +19,6 @@ import { ROUTES } from '../../routes'
|
|||||||
import { sleepMs } from '../../utils'
|
import { sleepMs } from '../../utils'
|
||||||
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
|
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
|
||||||
import { ResolvedWallet } from '../../utils/wallet'
|
import { ResolvedWallet } from '../../utils/wallet'
|
||||||
import { BeeModes } from '@ethersphere/bee-js'
|
|
||||||
|
|
||||||
export function GiftCardFund(): ReactElement {
|
export function GiftCardFund(): ReactElement {
|
||||||
const { nodeAddresses, nodeInfo } = useContext(BeeContext)
|
const { nodeAddresses, nodeInfo } = useContext(BeeContext)
|
||||||
@@ -52,7 +52,6 @@ export function GiftCardFund(): ReactElement {
|
|||||||
await sleepMs(5_000)
|
await sleepMs(5_000)
|
||||||
await upgradeToLightNode(desktopUrl, rpcProviderUrl)
|
await upgradeToLightNode(desktopUrl, rpcProviderUrl)
|
||||||
await restartBeeNode(desktopUrl)
|
await restartBeeNode(desktopUrl)
|
||||||
enqueueSnackbar('Upgraded to light node', { variant: 'success' })
|
|
||||||
navigate(ROUTES.RESTART_LIGHT)
|
navigate(ROUTES.RESTART_LIGHT)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error) // eslint-disable-line
|
console.error(error) // eslint-disable-line
|
||||||
|
|||||||
+134
-47
@@ -1,6 +1,5 @@
|
|||||||
import { BeeModes } from '@ethersphere/bee-js'
|
import { BeeModes } from '@ethersphere/bee-js'
|
||||||
import { Box, Typography } from '@material-ui/core'
|
import { Box, Typography } from '@material-ui/core'
|
||||||
import BigNumber from 'bignumber.js'
|
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
@@ -14,36 +13,42 @@ import { Loading } from '../../components/Loading'
|
|||||||
import { SwarmButton } from '../../components/SwarmButton'
|
import { SwarmButton } from '../../components/SwarmButton'
|
||||||
import { SwarmDivider } from '../../components/SwarmDivider'
|
import { SwarmDivider } from '../../components/SwarmDivider'
|
||||||
import { SwarmTextInput } from '../../components/SwarmTextInput'
|
import { SwarmTextInput } from '../../components/SwarmTextInput'
|
||||||
import { BzzToken } from '../../models/BzzToken'
|
import { BzzToken, BZZ_DECIMAL_PLACES } from '../../models/BzzToken'
|
||||||
import { DaiToken } from '../../models/DaiToken'
|
import { DaiToken } from '../../models/DaiToken'
|
||||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
|
||||||
import { Context as BeeContext } from '../../providers/Bee'
|
import { Context as BeeContext } from '../../providers/Bee'
|
||||||
import { Context as SettingsContext } from '../../providers/Settings'
|
import { Context as SettingsContext } from '../../providers/Settings'
|
||||||
|
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { sleepMs } from '../../utils'
|
import { sleepMs } from '../../utils'
|
||||||
import { getBzzPriceAsDai, performSwap, restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
|
import {
|
||||||
|
getBzzPriceAsDai,
|
||||||
|
getDesktopConfiguration,
|
||||||
|
performSwap,
|
||||||
|
restartBeeNode,
|
||||||
|
upgradeToLightNode,
|
||||||
|
} from '../../utils/desktop'
|
||||||
|
import { Rpc } from '../../utils/rpc'
|
||||||
|
import { isSwapError, SwapError, wrapWithSwapError } from '../../utils/SwapError'
|
||||||
import { TopUpProgressIndicator } from './TopUpProgressIndicator'
|
import { TopUpProgressIndicator } from './TopUpProgressIndicator'
|
||||||
|
|
||||||
const MINIMUM_XDAI = '0.1'
|
const MINIMUM_XDAI = '0.1'
|
||||||
const MINIMUM_XBZZ = '0.1'
|
const MINIMUM_XBZZ = '0.1'
|
||||||
|
|
||||||
|
const GENERIC_SWAP_FAILED_ERROR_MESSAGE = 'Failed to swap. The full error is printed to the console.'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
header: string
|
header: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPositiveDecimal(value: string): boolean {
|
|
||||||
try {
|
|
||||||
return new BigNumber(value).isPositive()
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Swap({ header }: Props): ReactElement {
|
export function Swap({ header }: Props): ReactElement {
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [hasSwapped, setSwapped] = useState(false)
|
const [hasSwapped, setSwapped] = useState(false)
|
||||||
const [userInputSwap, setUserInputSwap] = useState<string | null>(null)
|
const [userInputSwap, setUserInputSwap] = useState<string | null>(null)
|
||||||
const [price, setPrice] = useState(DaiToken.fromDecimal('0.6', 18))
|
const [price, setPrice] = useState(DaiToken.fromDecimal('0.6'))
|
||||||
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
const [daiToSwap, setDaiToSwap] = useState<DaiToken | null>(null)
|
||||||
|
const [bzzAfterSwap, setBzzAfterSwap] = useState<BzzToken | null>(null)
|
||||||
|
const [daiAfterSwap, setDaiAfterSwap] = useState<DaiToken | null>(null)
|
||||||
|
|
||||||
const { rpcProviderUrl, isDesktop, desktopUrl } = useContext(SettingsContext)
|
const { rpcProviderUrl, isDesktop, desktopUrl } = useContext(SettingsContext)
|
||||||
const { nodeAddresses, nodeInfo } = useContext(BeeContext)
|
const { nodeAddresses, nodeInfo } = useContext(BeeContext)
|
||||||
@@ -52,38 +57,77 @@ export function Swap({ header }: Props): ReactElement {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
|
|
||||||
|
// Fetch current price of BZZ
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
getBzzPriceAsDai(desktopUrl).then(setPrice).catch(console.error)
|
getBzzPriceAsDai(desktopUrl).then(setPrice).catch(console.error)
|
||||||
}, [desktopUrl])
|
}, [desktopUrl])
|
||||||
|
|
||||||
if (!balance || !nodeAddresses) {
|
// Set the initial xDAI to swap
|
||||||
|
useEffect(() => {
|
||||||
|
if (!balance || userInputSwap) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const minimumOptimalValue = DaiToken.fromDecimal('1').plusBaseUnits(MINIMUM_XDAI).toDecimal
|
||||||
|
|
||||||
|
if (balance.dai.toDecimal.isGreaterThanOrEqualTo(minimumOptimalValue)) {
|
||||||
|
// Balance has at least 1 + MINIMUM_XDAI xDai
|
||||||
|
setDaiToSwap(balance.dai.minusBaseUnits('1'))
|
||||||
|
} else {
|
||||||
|
// Balance is low, halve the amount
|
||||||
|
setDaiToSwap(new DaiToken(balance.dai.toBigNumber.dividedToIntegerBy(2)))
|
||||||
|
}
|
||||||
|
}, [balance, userInputSwap])
|
||||||
|
|
||||||
|
// Set the xDAI to swap based on user input
|
||||||
|
useEffect(() => {
|
||||||
|
setError(null)
|
||||||
|
try {
|
||||||
|
if (userInputSwap) {
|
||||||
|
const dai = DaiToken.fromDecimal(userInputSwap)
|
||||||
|
setDaiToSwap(dai)
|
||||||
|
|
||||||
|
if (dai.toDecimal.lte(0)) {
|
||||||
|
setError('xDAI to swap must be a positive number')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setError('Cannot parse xDAI amount')
|
||||||
|
}
|
||||||
|
}, [userInputSwap])
|
||||||
|
|
||||||
|
// Calculate the amount of tokens after swap
|
||||||
|
useEffect(() => {
|
||||||
|
if (!balance || !daiToSwap || error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const daiAfterSwap = new DaiToken(balance.dai.toBigNumber.minus(daiToSwap.toBigNumber))
|
||||||
|
setDaiAfterSwap(daiAfterSwap)
|
||||||
|
const tokensConverted = BzzToken.fromDecimal(
|
||||||
|
daiToSwap.toBigNumber.dividedBy(price.toBigNumber).decimalPlaces(BZZ_DECIMAL_PLACES),
|
||||||
|
)
|
||||||
|
const bzzAfterSwap = new BzzToken(tokensConverted.toBigNumber.plus(balance.bzz.toBigNumber))
|
||||||
|
setBzzAfterSwap(bzzAfterSwap)
|
||||||
|
|
||||||
|
if (daiAfterSwap.toDecimal.lt(MINIMUM_XDAI)) {
|
||||||
|
setError(`Must keep at least ${MINIMUM_XDAI} xDAI after swap!`)
|
||||||
|
} else if (bzzAfterSwap.toDecimal.lt(MINIMUM_XBZZ)) {
|
||||||
|
setError(`Must have at least ${MINIMUM_XBZZ} xBZZ after swap!`)
|
||||||
|
}
|
||||||
|
}, [error, balance, daiToSwap, price])
|
||||||
|
|
||||||
|
if (!balance || !nodeAddresses || !daiToSwap || !bzzAfterSwap || !daiAfterSwap) {
|
||||||
return <Loading />
|
return <Loading />
|
||||||
}
|
}
|
||||||
|
|
||||||
const optimalSwap = balance.dai.minusBaseUnits('1')
|
|
||||||
const lowAmountSwap = new DaiToken(balance.dai.toBigNumber.dividedToIntegerBy(2))
|
|
||||||
|
|
||||||
let daiToSwap: DaiToken
|
|
||||||
|
|
||||||
if (userInputSwap && isPositiveDecimal(userInputSwap)) {
|
|
||||||
daiToSwap = DaiToken.fromDecimal(userInputSwap, 18)
|
|
||||||
} else {
|
|
||||||
daiToSwap = lowAmountSwap.toBigNumber.gt(optimalSwap.toBigNumber) ? lowAmountSwap : optimalSwap
|
|
||||||
}
|
|
||||||
|
|
||||||
const daiAfterSwap = new DaiToken(balance.dai.toBigNumber.minus(daiToSwap.toBigNumber))
|
|
||||||
const bzzAfterSwap = new BzzToken(daiToSwap.toBigNumber.dividedBy(100).dividedToIntegerBy(price.toDecimal))
|
|
||||||
|
|
||||||
const canUpgradeToLightNode = isDesktop && nodeInfo?.beeMode === BeeModes.ULTRA_LIGHT
|
const canUpgradeToLightNode = isDesktop && nodeInfo?.beeMode === BeeModes.ULTRA_LIGHT
|
||||||
|
|
||||||
async function restart() {
|
async function restart() {
|
||||||
try {
|
try {
|
||||||
await sleepMs(5_000)
|
await sleepMs(5_000)
|
||||||
await upgradeToLightNode(desktopUrl, rpcProviderUrl)
|
|
||||||
await restartBeeNode(desktopUrl)
|
await restartBeeNode(desktopUrl)
|
||||||
|
|
||||||
enqueueSnackbar('Upgraded to light node', { variant: 'success' })
|
|
||||||
navigate(ROUTES.RESTART_LIGHT)
|
navigate(ROUTES.RESTART_LIGHT)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error) // eslint-disable-line
|
console.error(error) // eslint-disable-line
|
||||||
@@ -91,21 +135,71 @@ export function Swap({ header }: Props): ReactElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendSwapRequest(daiToSwap: DaiToken) {
|
||||||
|
try {
|
||||||
|
await performSwap(desktopUrl, daiToSwap.toString)
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function performSwapWithChecks(daiToSwap: DaiToken) {
|
||||||
|
if (!localStorage.getItem('apiKey')) {
|
||||||
|
throw new SwapError('API key is not set, reopen dashboard through Swarm Desktop')
|
||||||
|
}
|
||||||
|
|
||||||
|
let desktopConfiguration = await wrapWithSwapError(
|
||||||
|
getDesktopConfiguration(desktopUrl),
|
||||||
|
'Unable to reach Desktop API, Swarm Desktop may not be running',
|
||||||
|
)
|
||||||
|
|
||||||
|
if (canUpgradeToLightNode) {
|
||||||
|
desktopConfiguration = await wrapWithSwapError(
|
||||||
|
upgradeToLightNode(desktopUrl, rpcProviderUrl),
|
||||||
|
'Failed to update the configuration file with the new swap values using the Desktop API',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!desktopConfiguration['swap-endpoint']) {
|
||||||
|
throw new SwapError('Swap endpoint is not configured in Swarm Desktop')
|
||||||
|
}
|
||||||
|
await wrapWithSwapError(
|
||||||
|
Rpc.getNetworkChainId(desktopConfiguration['swap-endpoint']),
|
||||||
|
`Swap endpoint not reachable at ${desktopConfiguration['swap-endpoint']}`,
|
||||||
|
)
|
||||||
|
await wrapWithSwapError(sendSwapRequest(daiToSwap), GENERIC_SWAP_FAILED_ERROR_MESSAGE)
|
||||||
|
}
|
||||||
|
|
||||||
async function onSwap() {
|
async function onSwap() {
|
||||||
if (hasSwapped) {
|
if (hasSwapped || !daiToSwap) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
setSwapped(true)
|
setSwapped(true)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await performSwap(desktopUrl, daiToSwap.toString)
|
await performSwapWithChecks(daiToSwap)
|
||||||
enqueueSnackbar('Successfully swapped', { variant: 'success' })
|
const message = canUpgradeToLightNode
|
||||||
|
? 'Successfully swapped. Beginning light node upgrade...'
|
||||||
|
: 'Successfully swapped. Balances will refresh soon. You may now navigate away.'
|
||||||
|
enqueueSnackbar(message, { variant: 'success' })
|
||||||
|
|
||||||
if (canUpgradeToLightNode) await restart()
|
if (canUpgradeToLightNode) await restart()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error) // eslint-disable-line
|
if (isSwapError(error)) {
|
||||||
enqueueSnackbar(`Failed to swap: ${error}`, { variant: 'error' })
|
// we have a custom and user friendly error message
|
||||||
|
enqueueSnackbar(error.snackbarMessage, { variant: 'error' })
|
||||||
|
|
||||||
|
if (error.originalError) {
|
||||||
|
console.error(error.originalError) // eslint-disable-line
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we have an unexpected error
|
||||||
|
enqueueSnackbar(`${GENERIC_SWAP_FAILED_ERROR_MESSAGE} ${error}`, { variant: 'error' })
|
||||||
|
console.error(error) // eslint-disable-line
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
balance?.refresh()
|
balance?.refresh()
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
@@ -136,18 +230,13 @@ export function Swap({ header }: Props): ReactElement {
|
|||||||
</Box>
|
</Box>
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<SwarmTextInput
|
<SwarmTextInput
|
||||||
label="Amount to swap"
|
label="xDAI to swap"
|
||||||
defaultValue={`${daiToSwap.toSignificantDigits(4)} xDAI`}
|
defaultValue={daiToSwap.toSignificantDigits(4)}
|
||||||
placeholder={`${daiToSwap.toSignificantDigits(4)} xDAI`}
|
placeholder={daiToSwap.toSignificantDigits(4)}
|
||||||
name="x"
|
name="x"
|
||||||
onChange={event => setUserInputSwap(event.target.value)}
|
onChange={event => setUserInputSwap(event.target.value)}
|
||||||
/>
|
/>
|
||||||
{daiAfterSwap.toDecimal.lt(MINIMUM_XDAI) ? (
|
{error && <Typography>{error}</Typography>}
|
||||||
<Typography>Must keep at least {MINIMUM_XDAI} xDAI after swap!</Typography>
|
|
||||||
) : null}
|
|
||||||
{bzzAfterSwap.toDecimal.lt(MINIMUM_XBZZ) ? (
|
|
||||||
<Typography>Must have at least {MINIMUM_XBZZ} xBZZ after swap!</Typography>
|
|
||||||
) : null}
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<ArrowDown size={24} color="#aaaaaa" />
|
<ArrowDown size={24} color="#aaaaaa" />
|
||||||
@@ -171,9 +260,7 @@ export function Swap({ header }: Props): ReactElement {
|
|||||||
<SwarmButton
|
<SwarmButton
|
||||||
iconType={Check}
|
iconType={Check}
|
||||||
onClick={onSwap}
|
onClick={onSwap}
|
||||||
disabled={
|
disabled={hasSwapped || loading || error !== null}
|
||||||
hasSwapped || loading || daiAfterSwap.toDecimal.lt(MINIMUM_XDAI) || bzzAfterSwap.toDecimal.lt(MINIMUM_XBZZ)
|
|
||||||
}
|
|
||||||
loading={loading}
|
loading={loading}
|
||||||
>
|
>
|
||||||
{canUpgradeToLightNode ? 'Swap Now and Upgrade' : 'Swap Now'}
|
{canUpgradeToLightNode ? 'Swap Now and Upgrade' : 'Swap Now'}
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
|
import { BeeModes } from '@ethersphere/bee-js'
|
||||||
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
|
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
|
||||||
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useState } from 'react'
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
|
import { useNavigate } from 'react-router'
|
||||||
|
import BankCard from 'remixicon-react/BankCard2LineIcon'
|
||||||
import Check from 'remixicon-react/CheckLineIcon'
|
import Check from 'remixicon-react/CheckLineIcon'
|
||||||
import Download from 'remixicon-react/DownloadLineIcon'
|
import Download from 'remixicon-react/DownloadLineIcon'
|
||||||
import BankCard from 'remixicon-react/BankCard2LineIcon'
|
|
||||||
import MoneyDollarCircle from 'remixicon-react/MoneyDollarCircleLineIcon'
|
|
||||||
import Gift from 'remixicon-react/GiftLineIcon'
|
import Gift from 'remixicon-react/GiftLineIcon'
|
||||||
import { useNavigate } from 'react-router'
|
import MoneyDollarCircle from 'remixicon-react/MoneyDollarCircleLineIcon'
|
||||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||||
|
import { Loading } from '../../components/Loading'
|
||||||
import { SwarmButton } from '../../components/SwarmButton'
|
import { SwarmButton } from '../../components/SwarmButton'
|
||||||
import { ROUTES } from '../../routes'
|
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
||||||
import { Context as SettingsContext } from '../../providers/Settings'
|
import { Context as SettingsContext } from '../../providers/Settings'
|
||||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||||
import { BeeModes } from '@ethersphere/bee-js'
|
import { ROUTES } from '../../routes'
|
||||||
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
|
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
|
||||||
import { Loading } from '../../components/Loading'
|
|
||||||
import { useSnackbar } from 'notistack'
|
|
||||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
|
||||||
|
|
||||||
const useStyles = makeStyles(() =>
|
const useStyles = makeStyles(() =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -57,7 +57,6 @@ export default function TopUp(): ReactElement {
|
|||||||
try {
|
try {
|
||||||
await upgradeToLightNode(desktopUrl, rpcProviderUrl)
|
await upgradeToLightNode(desktopUrl, rpcProviderUrl)
|
||||||
await restartBeeNode(desktopUrl)
|
await restartBeeNode(desktopUrl)
|
||||||
enqueueSnackbar('Upgraded to light node', { variant: 'success' })
|
|
||||||
navigate(ROUTES.RESTART_LIGHT)
|
navigate(ROUTES.RESTART_LIGHT)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error) // eslint-disable-line
|
console.error(error) // eslint-disable-line
|
||||||
|
|||||||
+71
-22
@@ -13,18 +13,22 @@ import { createContext, ReactChild, ReactElement, useContext, useEffect, useStat
|
|||||||
import semver from 'semver'
|
import semver from 'semver'
|
||||||
import PackageJson from '../../package.json'
|
import PackageJson from '../../package.json'
|
||||||
import { useLatestBeeRelease } from '../hooks/apiHooks'
|
import { useLatestBeeRelease } from '../hooks/apiHooks'
|
||||||
|
import { BzzToken } from '../models/BzzToken'
|
||||||
import { Token } from '../models/Token'
|
import { Token } from '../models/Token'
|
||||||
import type { Balance, ChequebookBalance, Settlements } from '../types'
|
import type { Balance, ChequebookBalance, Settlements } from '../types'
|
||||||
import { Context as SettingsContext } from './Settings'
|
import { Context as SettingsContext } from './Settings'
|
||||||
|
|
||||||
|
const LAUNCH_GRACE_PERIOD = 15_000
|
||||||
const REFRESH_WHEN_OK = 30_000
|
const REFRESH_WHEN_OK = 30_000
|
||||||
const REFRESH_WHEN_ERROR = 5_000
|
const REFRESH_WHEN_ERROR = 5_000
|
||||||
const TIMEOUT = 3_000
|
const TIMEOUT = 3_000
|
||||||
|
|
||||||
export enum CheckState {
|
export enum CheckState {
|
||||||
|
CONNECTING = 'Connecting',
|
||||||
OK = 'OK',
|
OK = 'OK',
|
||||||
WARNING = 'Warning',
|
WARNING = 'Warning',
|
||||||
ERROR = 'Error',
|
ERROR = 'Error',
|
||||||
|
STARTING = 'Starting',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StatusItem {
|
interface StatusItem {
|
||||||
@@ -52,12 +56,14 @@ interface ContextInterface {
|
|||||||
error: Error | null
|
error: Error | null
|
||||||
apiHealth: boolean
|
apiHealth: boolean
|
||||||
debugApiHealth: Health | null
|
debugApiHealth: Health | null
|
||||||
|
debugApiReadiness: boolean
|
||||||
nodeAddresses: NodeAddresses | null
|
nodeAddresses: NodeAddresses | null
|
||||||
nodeInfo: NodeInfo | null
|
nodeInfo: NodeInfo | null
|
||||||
topology: Topology | null
|
topology: Topology | null
|
||||||
chequebookAddress: ChequebookAddressResponse | null
|
chequebookAddress: ChequebookAddressResponse | null
|
||||||
peers: Peer[] | null
|
peers: Peer[] | null
|
||||||
chequebookBalance: ChequebookBalance | null
|
chequebookBalance: ChequebookBalance | null
|
||||||
|
stake: BzzToken | null
|
||||||
peerBalances: Balance[] | null
|
peerBalances: Balance[] | null
|
||||||
peerCheques: LastChequesResponse | null
|
peerCheques: LastChequesResponse | null
|
||||||
settlements: Settlements | null
|
settlements: Settlements | null
|
||||||
@@ -89,10 +95,12 @@ const initialValues: ContextInterface = {
|
|||||||
error: null,
|
error: null,
|
||||||
apiHealth: false,
|
apiHealth: false,
|
||||||
debugApiHealth: null,
|
debugApiHealth: null,
|
||||||
|
debugApiReadiness: false,
|
||||||
nodeAddresses: null,
|
nodeAddresses: null,
|
||||||
nodeInfo: null,
|
nodeInfo: null,
|
||||||
topology: null,
|
topology: null,
|
||||||
chequebookAddress: null,
|
chequebookAddress: null,
|
||||||
|
stake: null,
|
||||||
peers: null,
|
peers: null,
|
||||||
chequebookBalance: null,
|
chequebookBalance: null,
|
||||||
peerBalances: null,
|
peerBalances: null,
|
||||||
@@ -117,25 +125,27 @@ interface Props {
|
|||||||
|
|
||||||
function getStatus(
|
function getStatus(
|
||||||
debugApiHealth: Health | null,
|
debugApiHealth: Health | null,
|
||||||
nodeAddresses: NodeAddresses | null,
|
debugApiReadiness: boolean,
|
||||||
nodeInfo: NodeInfo | null,
|
nodeInfo: NodeInfo | null,
|
||||||
apiHealth: boolean,
|
apiHealth: boolean,
|
||||||
topology: Topology | null,
|
topology: Topology | null,
|
||||||
chequebookAddress: ChequebookAddressResponse | null,
|
chequebookAddress: ChequebookAddressResponse | null,
|
||||||
chequebookBalance: ChequebookBalance | null,
|
chequebookBalance: ChequebookBalance | null,
|
||||||
error: Error | null,
|
error: Error | null,
|
||||||
|
isDesktop: boolean,
|
||||||
|
startedAt: number,
|
||||||
): Status {
|
): Status {
|
||||||
const status: Status = { ...initialValues.status }
|
const status: Status = { ...initialValues.status }
|
||||||
|
|
||||||
// Version check
|
// Version check
|
||||||
status.version.isEnabled = true
|
status.version.isEnabled = !isDesktop
|
||||||
status.version.checkState =
|
status.version.checkState =
|
||||||
debugApiHealth &&
|
debugApiHealth &&
|
||||||
semver.satisfies(debugApiHealth.version, PackageJson.engines.bee, {
|
semver.satisfies(debugApiHealth.version, PackageJson.engines.bee, {
|
||||||
includePrerelease: true,
|
includePrerelease: true,
|
||||||
})
|
})
|
||||||
? CheckState.OK
|
? CheckState.OK
|
||||||
: CheckState.ERROR
|
: CheckState.WARNING
|
||||||
|
|
||||||
// Blockchain connection check
|
// Blockchain connection check
|
||||||
status.blockchainConnection.isEnabled = true
|
status.blockchainConnection.isEnabled = true
|
||||||
@@ -159,48 +169,72 @@ function getStatus(
|
|||||||
if (error || (nodeInfo && [BeeModes.FULL, BeeModes.LIGHT].includes(nodeInfo.beeMode))) {
|
if (error || (nodeInfo && [BeeModes.FULL, BeeModes.LIGHT].includes(nodeInfo.beeMode))) {
|
||||||
status.chequebook.isEnabled = true
|
status.chequebook.isEnabled = true
|
||||||
|
|
||||||
if (
|
if (chequebookAddress?.chequebookAddress && chequebookBalance !== null) {
|
||||||
chequebookAddress?.chequebookAddress &&
|
|
||||||
chequebookBalance !== null &&
|
|
||||||
chequebookBalance?.totalBalance.toBigNumber.isGreaterThan(0)
|
|
||||||
) {
|
|
||||||
status.chequebook.checkState = CheckState.OK
|
status.chequebook.checkState = CheckState.OK
|
||||||
} else if (chequebookAddress?.chequebookAddress) status.chequebook.checkState = CheckState.WARNING
|
} else status.chequebook.checkState = CheckState.OK
|
||||||
else status.chequebook.checkState = CheckState.OK
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine overall status
|
status.all = determineOverallStatus(debugApiHealth, debugApiReadiness, status, startedAt)
|
||||||
if (Object.values(status).some(({ isEnabled, checkState }) => isEnabled && checkState === CheckState.ERROR)) {
|
|
||||||
status.all = CheckState.ERROR
|
|
||||||
} else if (
|
|
||||||
Object.values(status).some(({ isEnabled, checkState }) => isEnabled && checkState === CheckState.WARNING)
|
|
||||||
) {
|
|
||||||
status.all = CheckState.WARNING
|
|
||||||
} else {
|
|
||||||
status.all = CheckState.OK
|
|
||||||
}
|
|
||||||
|
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function determineOverallStatus(
|
||||||
|
debugApiHealth: Health | null,
|
||||||
|
debugApiReadiness: boolean,
|
||||||
|
status: Status,
|
||||||
|
startedAt: number,
|
||||||
|
): CheckState {
|
||||||
|
const hasErrors = Object.values(status).some(
|
||||||
|
({ isEnabled, checkState }) => isEnabled && checkState === CheckState.ERROR,
|
||||||
|
)
|
||||||
|
const hasWarnings = Object.values(status).some(
|
||||||
|
({ isEnabled, checkState }) => isEnabled && checkState === CheckState.WARNING,
|
||||||
|
)
|
||||||
|
const isInGracePeriod = Date.now() - startedAt < LAUNCH_GRACE_PERIOD
|
||||||
|
|
||||||
|
if (debugApiHealth?.status === 'ok' && !debugApiReadiness) {
|
||||||
|
return CheckState.STARTING
|
||||||
|
} else if (hasErrors && isInGracePeriod) {
|
||||||
|
return CheckState.CONNECTING
|
||||||
|
} else if (hasErrors) {
|
||||||
|
return CheckState.ERROR
|
||||||
|
} else if (hasWarnings) {
|
||||||
|
return CheckState.WARNING
|
||||||
|
} else {
|
||||||
|
return CheckState.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This does not need to be exposed and works much better as variable than state variable which may trigger some unnecessary re-renders
|
// This does not need to be exposed and works much better as variable than state variable which may trigger some unnecessary re-renders
|
||||||
let isRefreshing = false
|
let isRefreshing = false
|
||||||
|
|
||||||
export function Provider({ children }: Props): ReactElement {
|
interface InitialSettings {
|
||||||
|
isDesktop?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props extends InitialSettings {
|
||||||
|
children: ReactChild
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Provider({ children, isDesktop }: Props): ReactElement {
|
||||||
const { beeApi, beeDebugApi } = useContext(SettingsContext)
|
const { beeApi, beeDebugApi } = useContext(SettingsContext)
|
||||||
const [apiHealth, setApiHealth] = useState<boolean>(false)
|
const [apiHealth, setApiHealth] = useState<boolean>(false)
|
||||||
const [debugApiHealth, setDebugApiHealth] = useState<Health | null>(null)
|
const [debugApiHealth, setDebugApiHealth] = useState<Health | null>(null)
|
||||||
|
const [debugApiReadiness, setDebugApiReadiness] = useState(false)
|
||||||
const [nodeAddresses, setNodeAddresses] = useState<NodeAddresses | null>(null)
|
const [nodeAddresses, setNodeAddresses] = useState<NodeAddresses | null>(null)
|
||||||
const [nodeInfo, setNodeInfo] = useState<NodeInfo | null>(null)
|
const [nodeInfo, setNodeInfo] = useState<NodeInfo | null>(null)
|
||||||
const [topology, setNodeTopology] = useState<Topology | null>(null)
|
const [topology, setNodeTopology] = useState<Topology | null>(null)
|
||||||
const [chequebookAddress, setChequebookAddress] = useState<ChequebookAddressResponse | null>(null)
|
const [chequebookAddress, setChequebookAddress] = useState<ChequebookAddressResponse | null>(null)
|
||||||
const [peers, setPeers] = useState<Peer[] | null>(null)
|
const [peers, setPeers] = useState<Peer[] | null>(null)
|
||||||
const [chequebookBalance, setChequebookBalance] = useState<ChequebookBalance | null>(null)
|
const [chequebookBalance, setChequebookBalance] = useState<ChequebookBalance | null>(null)
|
||||||
|
const [stake, setStake] = useState<BzzToken | null>(null)
|
||||||
const [peerBalances, setPeerBalances] = useState<Balance[] | null>(null)
|
const [peerBalances, setPeerBalances] = useState<Balance[] | null>(null)
|
||||||
const [peerCheques, setPeerCheques] = useState<LastChequesResponse | null>(null)
|
const [peerCheques, setPeerCheques] = useState<LastChequesResponse | null>(null)
|
||||||
const [settlements, setSettlements] = useState<Settlements | null>(null)
|
const [settlements, setSettlements] = useState<Settlements | null>(null)
|
||||||
const [chainState, setChainState] = useState<ChainState | null>(null)
|
const [chainState, setChainState] = useState<ChainState | null>(null)
|
||||||
const [chainId, setChainId] = useState<number | null>(null)
|
const [chainId, setChainId] = useState<number | null>(null)
|
||||||
|
const [startedAt] = useState(Date.now())
|
||||||
|
|
||||||
const { latestBeeRelease } = useLatestBeeRelease()
|
const { latestBeeRelease } = useLatestBeeRelease()
|
||||||
|
|
||||||
@@ -299,6 +333,12 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
.then(setDebugApiHealth)
|
.then(setDebugApiHealth)
|
||||||
.catch(() => setDebugApiHealth(null)),
|
.catch(() => setDebugApiHealth(null)),
|
||||||
|
|
||||||
|
// Debug API readiness
|
||||||
|
beeDebugApi
|
||||||
|
.getReadiness({ timeout: TIMEOUT })
|
||||||
|
.then(setDebugApiReadiness)
|
||||||
|
.catch(() => setDebugApiReadiness(false)),
|
||||||
|
|
||||||
// Node Addresses
|
// Node Addresses
|
||||||
beeDebugApi
|
beeDebugApi
|
||||||
.getNodeAddresses({ timeout: TIMEOUT })
|
.getNodeAddresses({ timeout: TIMEOUT })
|
||||||
@@ -352,6 +392,11 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
.then(setChequebookBalance)
|
.then(setChequebookBalance)
|
||||||
.catch(() => setChequebookBalance(null)),
|
.catch(() => setChequebookBalance(null)),
|
||||||
|
|
||||||
|
beeDebugApi
|
||||||
|
.getStake({ timeout: TIMEOUT })
|
||||||
|
.then(stake => setStake(new BzzToken(stake)))
|
||||||
|
.catch(() => setStake(null)),
|
||||||
|
|
||||||
// Peer balances
|
// Peer balances
|
||||||
peerBalanceWrapper()
|
peerBalanceWrapper()
|
||||||
.then(setPeerBalances)
|
.then(setPeerBalances)
|
||||||
@@ -381,13 +426,15 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
|
|
||||||
const status = getStatus(
|
const status = getStatus(
|
||||||
debugApiHealth,
|
debugApiHealth,
|
||||||
nodeAddresses,
|
debugApiReadiness,
|
||||||
nodeInfo,
|
nodeInfo,
|
||||||
apiHealth,
|
apiHealth,
|
||||||
topology,
|
topology,
|
||||||
chequebookAddress,
|
chequebookAddress,
|
||||||
chequebookBalance,
|
chequebookBalance,
|
||||||
error,
|
error,
|
||||||
|
Boolean(isDesktop),
|
||||||
|
startedAt,
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -426,12 +473,14 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
error,
|
error,
|
||||||
apiHealth,
|
apiHealth,
|
||||||
debugApiHealth,
|
debugApiHealth,
|
||||||
|
debugApiReadiness,
|
||||||
nodeAddresses,
|
nodeAddresses,
|
||||||
nodeInfo,
|
nodeInfo,
|
||||||
topology,
|
topology,
|
||||||
chequebookAddress,
|
chequebookAddress,
|
||||||
peers,
|
peers,
|
||||||
chequebookBalance,
|
chequebookBalance,
|
||||||
|
stake,
|
||||||
peerBalances,
|
peerBalances,
|
||||||
peerCheques,
|
peerCheques,
|
||||||
settlements,
|
settlements,
|
||||||
|
|||||||
+5
-1
@@ -2,6 +2,7 @@ import { ReactElement, useContext } from 'react'
|
|||||||
import { Route, Routes } from 'react-router-dom'
|
import { Route, Routes } from 'react-router-dom'
|
||||||
import { AccountChequebook } from './pages/account/chequebook/AccountChequebook'
|
import { AccountChequebook } from './pages/account/chequebook/AccountChequebook'
|
||||||
import { AccountFeeds } from './pages/account/feeds/AccountFeeds'
|
import { AccountFeeds } from './pages/account/feeds/AccountFeeds'
|
||||||
|
import { AccountStaking } from './pages/account/staking/AccountStaking'
|
||||||
import { AccountStamps } from './pages/account/stamps/AccountStamps'
|
import { AccountStamps } from './pages/account/stamps/AccountStamps'
|
||||||
import { AccountWallet } from './pages/account/wallet/AccountWallet'
|
import { AccountWallet } from './pages/account/wallet/AccountWallet'
|
||||||
import CreateNewFeed from './pages/feeds/CreateNewFeed'
|
import CreateNewFeed from './pages/feeds/CreateNewFeed'
|
||||||
@@ -14,10 +15,10 @@ import { UploadLander } from './pages/files/UploadLander'
|
|||||||
import GiftCards from './pages/gift-code'
|
import GiftCards from './pages/gift-code'
|
||||||
import Info from './pages/info'
|
import Info from './pages/info'
|
||||||
import LightModeRestart from './pages/restart/LightModeRestart'
|
import LightModeRestart from './pages/restart/LightModeRestart'
|
||||||
import TopUp from './pages/top-up'
|
|
||||||
import Settings from './pages/settings'
|
import Settings from './pages/settings'
|
||||||
import { CreatePostageStampPage } from './pages/stamps/CreatePostageStampPage'
|
import { CreatePostageStampPage } from './pages/stamps/CreatePostageStampPage'
|
||||||
import Status from './pages/status'
|
import Status from './pages/status'
|
||||||
|
import TopUp from './pages/top-up'
|
||||||
import { BankCardTopUpIndex } from './pages/top-up/BankCardTopUpIndex'
|
import { BankCardTopUpIndex } from './pages/top-up/BankCardTopUpIndex'
|
||||||
import { CryptoTopUpIndex } from './pages/top-up/CryptoTopUpIndex'
|
import { CryptoTopUpIndex } from './pages/top-up/CryptoTopUpIndex'
|
||||||
import { GiftCardFund } from './pages/top-up/GiftCardFund'
|
import { GiftCardFund } from './pages/top-up/GiftCardFund'
|
||||||
@@ -51,6 +52,7 @@ export enum ROUTES {
|
|||||||
ACCOUNT_FEEDS_UPDATE = '/account/feeds/update/:hash',
|
ACCOUNT_FEEDS_UPDATE = '/account/feeds/update/:hash',
|
||||||
ACCOUNT_FEEDS_VIEW = '/account/feeds/:uuid',
|
ACCOUNT_FEEDS_VIEW = '/account/feeds/:uuid',
|
||||||
ACCOUNT_INVITATIONS = '/account/invitations',
|
ACCOUNT_INVITATIONS = '/account/invitations',
|
||||||
|
ACCOUNT_STAKING = '/account/staking',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ACCOUNT_TABS = [
|
export const ACCOUNT_TABS = [
|
||||||
@@ -58,6 +60,7 @@ export const ACCOUNT_TABS = [
|
|||||||
ROUTES.ACCOUNT_CHEQUEBOOK,
|
ROUTES.ACCOUNT_CHEQUEBOOK,
|
||||||
ROUTES.ACCOUNT_STAMPS,
|
ROUTES.ACCOUNT_STAMPS,
|
||||||
ROUTES.ACCOUNT_FEEDS,
|
ROUTES.ACCOUNT_FEEDS,
|
||||||
|
ROUTES.ACCOUNT_STAKING,
|
||||||
]
|
]
|
||||||
|
|
||||||
const BaseRouter = (): ReactElement => {
|
const BaseRouter = (): ReactElement => {
|
||||||
@@ -88,6 +91,7 @@ const BaseRouter = (): ReactElement => {
|
|||||||
<Route path={ROUTES.ACCOUNT_FEEDS_NEW} element={<CreateNewFeed />} />
|
<Route path={ROUTES.ACCOUNT_FEEDS_NEW} element={<CreateNewFeed />} />
|
||||||
<Route path={ROUTES.ACCOUNT_FEEDS_UPDATE} element={<UpdateFeed />} />
|
<Route path={ROUTES.ACCOUNT_FEEDS_UPDATE} element={<UpdateFeed />} />
|
||||||
<Route path={ROUTES.ACCOUNT_FEEDS_VIEW} element={<FeedSubpage />} />
|
<Route path={ROUTES.ACCOUNT_FEEDS_VIEW} element={<FeedSubpage />} />
|
||||||
|
<Route path={ROUTES.ACCOUNT_STAKING} element={<AccountStaking />} />
|
||||||
{isDesktop && <Route path={ROUTES.ACCOUNT_INVITATIONS} element={<GiftCards />} />}
|
{isDesktop && <Route path={ROUTES.ACCOUNT_INVITATIONS} element={<GiftCards />} />}
|
||||||
</Routes>
|
</Routes>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export class AuthError extends Error {
|
||||||
|
constructor() {
|
||||||
|
super('Bad API key')
|
||||||
|
this.name = 'AuthError'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAuthError(error: unknown): error is AuthError {
|
||||||
|
return error instanceof Error && error.name === 'AuthError'
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { isAuthError } from './AuthError'
|
||||||
|
|
||||||
|
export class SwapError extends Error {
|
||||||
|
snackbarMessage: string
|
||||||
|
originalError?: Error
|
||||||
|
|
||||||
|
constructor(snackbarMessage: string, error?: Error) {
|
||||||
|
super(error?.message || snackbarMessage)
|
||||||
|
this.name = 'SwapError'
|
||||||
|
this.originalError = error
|
||||||
|
this.snackbarMessage = snackbarMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSwapError(error: unknown): error is SwapError {
|
||||||
|
return error instanceof Error && error.name === 'SwapError'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wrapWithSwapError<T>(promise: Promise<T>, snackbarMessage: string): Promise<T> {
|
||||||
|
return promise.catch((error: Error) => {
|
||||||
|
if (isAuthError(error)) {
|
||||||
|
throw new SwapError('Bad API key, reopen dashboard through Swarm Desktop', error)
|
||||||
|
}
|
||||||
|
throw new SwapError(snackbarMessage, error)
|
||||||
|
})
|
||||||
|
}
|
||||||
+27
-7
@@ -1,17 +1,33 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import { BEE_DESKTOP_LATEST_RELEASE_PAGE_API } from '../constants'
|
||||||
import { DaiToken } from '../models/DaiToken'
|
import { DaiToken } from '../models/DaiToken'
|
||||||
import { Token } from '../models/Token'
|
import { Token } from '../models/Token'
|
||||||
import { postJson } from './net'
|
import { getJson, postJson } from './net'
|
||||||
import { BEE_DESKTOP_LATEST_RELEASE_PAGE_API } from '../constants'
|
|
||||||
|
export interface BeeConfig {
|
||||||
|
'api-addr': string
|
||||||
|
'debug-api-addr': string
|
||||||
|
'debug-api-enable': boolean
|
||||||
|
password: string
|
||||||
|
'swap-enable': boolean
|
||||||
|
'swap-initial-deposit': bigint
|
||||||
|
mainnet: boolean
|
||||||
|
'full-node': boolean
|
||||||
|
'cors-allowed-origins': string
|
||||||
|
'resolver-options': string
|
||||||
|
'use-postage-snapshot': boolean
|
||||||
|
'data-dir': string
|
||||||
|
'swap-endpoint'?: string
|
||||||
|
}
|
||||||
|
|
||||||
export async function getBzzPriceAsDai(desktopUrl: string): Promise<Token> {
|
export async function getBzzPriceAsDai(desktopUrl: string): Promise<Token> {
|
||||||
const response = await axios.get(`${desktopUrl}/price`)
|
const response = await axios.get(`${desktopUrl}/price`)
|
||||||
|
|
||||||
return DaiToken.fromDecimal(response.data, 18)
|
return DaiToken.fromDecimal(response.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function upgradeToLightNode(desktopUrl: string, rpcProvider: string): Promise<void> {
|
export function upgradeToLightNode(desktopUrl: string, rpcProvider: string): Promise<BeeConfig> {
|
||||||
await updateDesktopConfiguration(desktopUrl, {
|
return updateDesktopConfiguration(desktopUrl, {
|
||||||
'swap-enable': true,
|
'swap-enable': true,
|
||||||
'swap-endpoint': rpcProvider,
|
'swap-endpoint': rpcProvider,
|
||||||
})
|
})
|
||||||
@@ -23,8 +39,12 @@ export async function setJsonRpcInDesktop(desktopUrl: string, value: string): Pr
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateDesktopConfiguration(desktopUrl: string, values: Record<string, unknown>): Promise<void> {
|
export function getDesktopConfiguration(desktopUrl: string): Promise<BeeConfig> {
|
||||||
await postJson(`${desktopUrl}/config`, values)
|
return getJson(`${desktopUrl}/config`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDesktopConfiguration(desktopUrl: string, values: Record<string, unknown>): Promise<BeeConfig> {
|
||||||
|
return postJson(`${desktopUrl}/config`, values)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function restartBeeNode(desktopUrl: string): Promise<void> {
|
export async function restartBeeNode(desktopUrl: string): Promise<void> {
|
||||||
|
|||||||
+11
-1
@@ -1,12 +1,13 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import { AuthError } from './AuthError'
|
||||||
|
|
||||||
export function getJson<T extends Record<string, any>>(url: string): Promise<T> {
|
export function getJson<T extends Record<string, any>>(url: string): Promise<T> {
|
||||||
return sendRequest(url, 'GET') as Promise<T>
|
return sendRequest(url, 'GET') as Promise<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function postJson<T extends Record<string, any>>(url: string, data?: T): Promise<T> {
|
export function postJson<T extends Record<string, any>>(url: string, data?: Record<string, any>): Promise<T> {
|
||||||
return sendRequest(url, 'POST', data) as Promise<T>
|
return sendRequest(url, 'POST', data) as Promise<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +28,15 @@ export async function sendRequest(
|
|||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
data,
|
data,
|
||||||
|
}).catch(error => {
|
||||||
|
if (error?.response?.status === 401) {
|
||||||
|
throw new AuthError()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error?.response?.data) {
|
||||||
|
throw Error(`Request ${method} ${url} failed: ${JSON.stringify(error.response.data)}`)
|
||||||
|
}
|
||||||
|
throw error
|
||||||
})
|
})
|
||||||
|
|
||||||
return response.data
|
return response.data
|
||||||
|
|||||||
+12
-1
@@ -2,6 +2,16 @@ import { debounce } from '@material-ui/core'
|
|||||||
import { Contract, providers, Wallet, BigNumber as BN } from 'ethers'
|
import { Contract, providers, Wallet, BigNumber as BN } from 'ethers'
|
||||||
import { bzzABI, BZZ_TOKEN_ADDRESS } from './bzz-abi'
|
import { bzzABI, BZZ_TOKEN_ADDRESS } from './bzz-abi'
|
||||||
|
|
||||||
|
const NETWORK_ID = 100
|
||||||
|
|
||||||
|
async function getNetworkChainId(url: string): Promise<number> {
|
||||||
|
const provider = new providers.JsonRpcProvider(url, NETWORK_ID)
|
||||||
|
await provider.ready
|
||||||
|
const network = await provider.getNetwork()
|
||||||
|
|
||||||
|
return network.chainId
|
||||||
|
}
|
||||||
|
|
||||||
async function eth_getBalance(address: string, provider: providers.JsonRpcProvider): Promise<string> {
|
async function eth_getBalance(address: string, provider: providers.JsonRpcProvider): Promise<string> {
|
||||||
if (!address.startsWith('0x')) {
|
if (!address.startsWith('0x')) {
|
||||||
address = `0x${address}`
|
address = `0x${address}`
|
||||||
@@ -78,7 +88,7 @@ export async function sendBzzTransaction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function makeReadySigner(privateKey: string, jsonRpcProvider: string) {
|
async function makeReadySigner(privateKey: string, jsonRpcProvider: string) {
|
||||||
const provider = new providers.JsonRpcProvider(jsonRpcProvider, 100)
|
const provider = new providers.JsonRpcProvider(jsonRpcProvider, NETWORK_ID)
|
||||||
await provider.ready
|
await provider.ready
|
||||||
const signer = new Wallet(privateKey, provider)
|
const signer = new Wallet(privateKey, provider)
|
||||||
|
|
||||||
@@ -86,6 +96,7 @@ async function makeReadySigner(privateKey: string, jsonRpcProvider: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Rpc = {
|
export const Rpc = {
|
||||||
|
getNetworkChainId,
|
||||||
sendNativeTransaction,
|
sendNativeTransaction,
|
||||||
sendBzzTransaction,
|
sendBzzTransaction,
|
||||||
_eth_getBalance: eth_getBalance,
|
_eth_getBalance: eth_getBalance,
|
||||||
|
|||||||
Reference in New Issue
Block a user