Compare commits

...

19 Commits

Author SHA1 Message Date
bee-worker 643f3b24db chore: release 0.8.0 (#205)
* chore: release 0.8.0

* chore: update changelog

Co-authored-by: Vojtech Simetka <vojtech@simetka.cz>
2021-10-20 19:43:39 +02:00
Vojtech Simetka 605054895d feat: updated readme to new bee version and UI examples with new design (#230) 2021-10-20 16:24:43 +02:00
Vojtech Simetka d5649dc8c6 chore(deps): update to bee-js 2.1.0 to support 2.0.0 (#229) 2021-10-14 15:59:25 +02:00
Vojtech Simetka cc5e778f89 fix: typo in population text (#228) 2021-10-13 13:20:06 +02:00
Vojtech Simetka f11bbd5008 fix: unknown routes should point to info page (#227) 2021-10-13 11:38:09 +02:00
Vojtech Simetka b4c9d9e018 fix: size of the troubleshoot component button (#226) 2021-10-13 11:37:59 +02:00
Vojtech Simetka 6c3f6c1019 fix: hover state style of ListItems which are clickable to be in line with other buttons (#223)
* fix: hover state style of ListItems which are clickable to be in line with other buttons

* fix: hover state of the SideBarStatus button
2021-10-11 10:07:31 +02:00
Vojtech Simetka 83c6d13417 fix: style of the update bee version button (#222)
* fix: style of the update bee version button

* chore: remove unused import
2021-10-11 10:07:06 +02:00
Vojtech Simetka 93af7f35a3 feat: files page updated to latest design (#218)
* feat: altered the design of the tabs and redid the download tab

* feat: redesign the upload file

* fix: styles of tabs on hover

* fix: display troubleshoot component when the status of the node is not OK

* fix: when removing the file, remove the reference upload reference as well

* fix: on inputs the label should not be selectable

* feat: add placeholder to inputs and make the label non-selectable

* refactor: improved the readability of the upload file component

* chore: removed PeerDetail component

* fix: replaced "batch" with (postage) "stamp" for clarity

* refactor: address PR review comments

* feat: disable the download button if there is no value
2021-10-08 13:34:55 +02:00
Attila Gazso 03265687ad fix: used label in postage stamp list (#220) 2021-10-08 08:38:12 +02:00
Vojtech Simetka f241b2fc5f feat: updated the postage stamps page design (#217)
* feat: updated the settings page design

* chore: removed unused dependencies
2021-10-07 12:22:46 +02:00
Vojtech Simetka 32e5ea9e56 feat: updated the settings page design (#215) 2021-10-07 10:47:51 +02:00
Vojtech Simetka b666cd2657 feat: status page redesign (#214)
* feat: initial rewrite without status indicators

* feat: status icon as a component and add to the node setup

* feat: added input list item component

* feat: improved the topology status info

* fix: disabled state of the buttons

* chore: removed unused components

* chore: remove debug console log

* fix: deposit modal helper text
2021-10-06 18:38:54 +02:00
Vojtech Simetka ecbc116475 feat: update design of the accounting page (#209)
* feat: update design of the accounting page, fixed the worsed graphical offenders

* chore: button alignment

* chore: removed unused dependency

* chore: buttons are underneath the action

* feat: refactored the peers table to be in line with the new design

* feat: add total uncashed amount and sorting for the peers

* feat: action buttons are now properly aligned

* chore: typo in comment
2021-10-05 12:59:08 +02:00
Attila Gazso e7188f4a35 fix: wording in chequebook setup (#211)
* fix: wording in chequebook setup

* fix: code linting

Co-authored-by: Vojtech Simetka <vojtech@simetka.cz>
2021-10-04 17:06:53 +02:00
Vojtech Simetka b69e368f69 feat: updated the settings page to be a bit more consistent with the rest of the design (#208) 2021-10-04 12:26:34 +02:00
Vojtech Simetka 57f5a73f3a feat: info page redesign (#207)
* feat: info page redesign

* style: headers and nesting for expandable lists

* style: body typography

* chore: bee version check

* feat: key list item component

* style: hover state for the key displaying component

* style: left border on expanded items

* fix: word break and splitting for hexstrings divisible by 6

* chore: moved the styles to classes

* style: tooltips now have arrow

* feat: indicate value has been copied

* feat: removed the connectivity table in favor of info page

* feat: added health tooltips for connectivity

* refactor: simplified the topology into single component

* fix: spacing between the bee version and the update button/chip

* fix: spacing on devices not supporting rowGap
2021-10-04 12:26:13 +02:00
Vojtech Simetka c4c7d9619d chore(deps): update bee-js to 2.0.0 (#210) 2021-10-04 12:25:41 +02:00
Vojtech Simetka c4c1573263 feat: troubleshooting component (#204)
* feat: troubleshooting component and general layout improvements

* style: background color, links and button

* chore: disable ripples and rounded corners on buttons

* fix: spacing on the troubleshooting card
2021-09-13 12:25:01 +02:00
61 changed files with 1654 additions and 1609 deletions
+27
View File
@@ -1,5 +1,32 @@
# Changelog
## [0.8.0](https://www.github.com/ethersphere/bee-dashboard/compare/v0.7.0...v0.8.0) (2021-10-20)
In this version we are adding support for the bee release 1.2.0. The app also went through a graphical redesign. More to come soon!
### Features
* support for bee 1.2.0
* update files page design ([#218](https://www.github.com/ethersphere/bee-dashboard/issues/218)) ([93af7f3](https://www.github.com/ethersphere/bee-dashboard/commit/93af7f35a371d54864c068be6e1d8a70092afe28))
* update info page design ([#207](https://www.github.com/ethersphere/bee-dashboard/issues/207)) ([57f5a73](https://www.github.com/ethersphere/bee-dashboard/commit/57f5a73f3a8d957bf967c51612dc09c802bb68dc))
* update status page design ([#214](https://www.github.com/ethersphere/bee-dashboard/issues/214)) ([b666cd2](https://www.github.com/ethersphere/bee-dashboard/commit/b666cd2657cf1003651c44b6b4fa5bdcf11e895f))
* update troubleshooting component design ([#204](https://www.github.com/ethersphere/bee-dashboard/issues/204)) ([c4c1573](https://www.github.com/ethersphere/bee-dashboard/commit/c4c1573263868b6dc8a863124e4aee824dceadbb))
* update accounting page design ([#209](https://www.github.com/ethersphere/bee-dashboard/issues/209)) ([ecbc116](https://www.github.com/ethersphere/bee-dashboard/commit/ecbc1164756de912d14ce44aa9b2c155dded6dac))
* update postage stamps page design ([#217](https://www.github.com/ethersphere/bee-dashboard/issues/217)) ([f241b2f](https://www.github.com/ethersphere/bee-dashboard/commit/f241b2fc5f6ec0741e275498ebef5a18ce710b81))
* update settings page design ([#215](https://www.github.com/ethersphere/bee-dashboard/issues/215)) ([32e5ea9](https://www.github.com/ethersphere/bee-dashboard/commit/32e5ea9e56fdf957b758ec714bb6a4fe1903082a))
### Bug Fixes
* hover state style of ListItems which are clickable to be in line with other buttons ([#223](https://www.github.com/ethersphere/bee-dashboard/issues/223)) ([6c3f6c1](https://www.github.com/ethersphere/bee-dashboard/commit/6c3f6c1019801267aa5e51002f6e21f769edc210))
* size of the troubleshoot component button ([#226](https://www.github.com/ethersphere/bee-dashboard/issues/226)) ([b4c9d9e](https://www.github.com/ethersphere/bee-dashboard/commit/b4c9d9e0182c4bee5ebb2d4e43e0aaad2aeb616b))
* style of the update bee version button ([#222](https://www.github.com/ethersphere/bee-dashboard/issues/222)) ([83c6d13](https://www.github.com/ethersphere/bee-dashboard/commit/83c6d1341790d664c7986dd2a816fe6a3b069e5c))
* typo in population text ([#228](https://www.github.com/ethersphere/bee-dashboard/issues/228)) ([cc5e778](https://www.github.com/ethersphere/bee-dashboard/commit/cc5e778f892b73b0b7ff5e0fa00c4816f3298ac7))
* unknown routes should point to info page ([#227](https://www.github.com/ethersphere/bee-dashboard/issues/227)) ([f11bbd5](https://www.github.com/ethersphere/bee-dashboard/commit/f11bbd5008a78ef7d5c73fc2758ee4e2dafae01e))
* used label in postage stamp list ([#220](https://www.github.com/ethersphere/bee-dashboard/issues/220)) ([0326568](https://www.github.com/ethersphere/bee-dashboard/commit/03265687ad630b0100da3134518b680327af1636))
* wording in chequebook setup ([#211](https://www.github.com/ethersphere/bee-dashboard/issues/211)) ([e7188f4](https://www.github.com/ethersphere/bee-dashboard/commit/e7188f4a35c85204eef6a01ae6f1e679d076180c))
## [0.7.0](https://www.github.com/ethersphere/bee-dashboard/compare/v0.6.0...v0.7.0) (2021-08-31)
+18 -10
View File
@@ -6,18 +6,21 @@
![](https://img.shields.io/badge/npm-%3E%3D6.0.0-orange.svg?style=flat-square)
![](https://img.shields.io/badge/Node.js-%3E%3D10.0.0-orange.svg?style=flat-square)
> An app which helps users to setup their Bee node and do actions like cash out cheques, upload and download files or manage your postage stamps.
> An app which helps users to setup their Bee node and do actions like cash out cheques, upload and download files or
> manage your postage stamps.
**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.**
**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.**
This project is intended to be used with **Bee version 1.1.0**. 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 [releases tab](https://github.com/ethersphere/bee-dashboard/releases).
This project is intended to be used with the latest released version of Bee. Using it with older 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 [releases tab](https://github.com/ethersphere/bee-dashboard/releases).
![Status page](/ui_samples/status.png)
| Node Setup | Browse & Upload Files | Accounting | Peers | Settings |
|-------|---------|-------|----------|------|
| ![Setup](/ui_samples/node_setup.png) | ![Files](/ui_samples/file_upload.png) | ![Accounting](/ui_samples/accounting.png) | ![Peers](/ui_samples/peers.png) | ![Settings](/ui_samples/settings.png) |
![Status page](/ui_samples/info.png)
| Node Setup | Upload Files | Download Content | Accounting | Postage Stamps |
| ------------------------------------ | -------------------------------------- | ------------------------------------------ | ----------------------------------------- | ---------------------------------------- |
| ![Setup](/ui_samples/node_setup.png) | ![Upload](/ui_samples/file_upload.png) | ![Download](/ui_samples/file_download.png) | ![Accounting](/ui_samples/accounting.png) | ![Peers](/ui_samples/postage_stamps.png) |
## Table of Contents
@@ -40,11 +43,15 @@ npm install -g @ethersphere/bee-dashboard
## Usage
:warning: To successfully connect to the Bee node, you will need to enable the Debug API and CORS. You can do so by setting `cors-allowed-origins: ['*']` and `debug-api-enable: true` in the Bee config file and then restart the Bee node. To see where the config file is, consult the [official Bee documentation](https://docs.ethswarm.org/docs/working-with-bee/configuration#configuring-bee-installed-using-a-package-manager)
:warning: To successfully connect to the Bee node, you will need to enable the Debug API and CORS. You can do so by
setting `cors-allowed-origins: ['*']` and `debug-api-enable: true` in the Bee config file and then restart the Bee node.
To see where the config file is, consult the
[official Bee documentation](https://docs.ethswarm.org/docs/working-with-bee/configuration#configuring-bee-installed-using-a-package-manager)
### Terminal
To start use:
```sh
bee-dashboard
```
@@ -82,7 +89,8 @@ There are some ways you can make this module better:
- Consult our [open issues](https://github.com/ethersphere/bee-dashboard/issues) and take on one of them
- Help our tests reach 100% coverage!
- Join us in our [Discord chat](https://discord.gg/wdghaQsGq5) in the #develop-on-swarm channel if you have questions or want to give feedback
- Join us in our [Discord chat](https://discord.gg/wdghaQsGq5) in the #develop-on-swarm channel if you have questions or
want to give feedback
## Maintainers
+231 -44
View File
@@ -1,15 +1,15 @@
{
"name": "@ethersphere/bee-dashboard",
"version": "0.7.0",
"version": "0.8.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ethersphere/bee-dashboard",
"version": "0.7.0",
"version": "0.8.0",
"license": "BSD-3-Clause",
"dependencies": {
"@ethersphere/bee-js": "1.2.1",
"@ethersphere/bee-js": "2.1.0",
"@material-ui/core": "4.12.3",
"@material-ui/icons": "4.11.2",
"@material-ui/lab": "4.0.0-alpha.57",
@@ -2097,19 +2097,22 @@
}
},
"node_modules/@ethersphere/bee-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-1.2.1.tgz",
"integrity": "sha512-Z1lh016EB8bxTNHUDXCDEcJN8IrPaz+vlIz89oscJF9q3Xuevmo/QCn7fQ0dqrppB8Motz7gjPteFJk5glsNGQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-2.1.0.tgz",
"integrity": "sha512-G33p+dGwSclIwG1dUKIfgACSHJouGh7LN2BOAeTg4Ts2Yg3Z/G+LzxCodyQpowLt0PaSTPjP2a/yER4NHoDUpg==",
"dependencies": {
"axios": "^0.21.1",
"cross-blob": "^2.0.1",
"elliptic": "^6.5.4",
"isomorphic-ws": "^4.0.1",
"js-sha3": "^0.8.0",
"ky": "^0.25.1",
"ky-universal": "^0.8.2",
"tar-js": "^0.3.0",
"web-streams-polyfill": "^3.1.0",
"ws": "^7.5.0"
},
"engines": {
"bee": "1.1.0-80cdea19",
"bee": "1.2.0-29eb9414",
"node": ">=12.0.0",
"npm": ">=6.0.0"
}
@@ -4472,6 +4475,17 @@
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
"dev": true
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@@ -4682,9 +4696,9 @@
}
},
"node_modules/ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
@@ -5819,6 +5833,11 @@
"file-uri-to-path": "1.0.0"
}
},
"node_modules/blob-polyfill": {
"version": "5.0.20210201",
"resolved": "https://registry.npmjs.org/blob-polyfill/-/blob-polyfill-5.0.20210201.tgz",
"integrity": "sha512-SrH6IG6aXL9pCgSysBCiDpGcAJ1j6/c1qCwR3sTEQJhb+MTk6FITNA6eW6WNYQDNZVi4Z9GjxH5v2MMTv59CrQ=="
},
"node_modules/bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@@ -7110,6 +7129,18 @@
"sha.js": "^2.4.8"
}
},
"node_modules/cross-blob": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/cross-blob/-/cross-blob-2.0.1.tgz",
"integrity": "sha512-ARuKPPo3I6DSqizal4UCyMCiGPQdMpMJS3Owx6Lleuh26vSt2UnfWRwbMLCYqbJUrcol+KzGVSLR91ezSHP80A==",
"dependencies": {
"blob-polyfill": "^5.0.20210201",
"fetch-blob": "^2.1.2"
},
"engines": {
"node": "^10.17.0 || >=12.3.0"
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -7597,6 +7628,14 @@
"integrity": "sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw==",
"dev": true
},
"node_modules/data-uri-to-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
"integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==",
"engines": {
"node": ">= 6"
}
},
"node_modules/data-urls": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
@@ -9353,6 +9392,14 @@
"node": ">= 0.6"
}
},
"node_modules/event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
@@ -9875,6 +9922,19 @@
"core-js": "^3.1.3"
}
},
"node_modules/fetch-blob": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz",
"integrity": "sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow==",
"engines": {
"node": "^10.17.0 || >=12.3.0"
},
"peerDependenciesMeta": {
"domexception": {
"optional": true
}
}
},
"node_modules/figgy-pudding": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
@@ -14400,6 +14460,41 @@
"node": ">= 8"
}
},
"node_modules/ky": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/ky/-/ky-0.25.1.tgz",
"integrity": "sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sindresorhus/ky?sponsor=1"
}
},
"node_modules/ky-universal": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.8.2.tgz",
"integrity": "sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==",
"dependencies": {
"abort-controller": "^3.0.0",
"node-fetch": "3.0.0-beta.9"
},
"engines": {
"node": ">=10.17"
},
"funding": {
"url": "https://github.com/sindresorhus/ky-universal?sponsor=1"
},
"peerDependencies": {
"ky": ">=0.17.0",
"web-streams-polyfill": ">=2.0.0"
},
"peerDependenciesMeta": {
"web-streams-polyfill": {
"optional": true
}
}
},
"node_modules/language-subtag-registry": {
"version": "0.3.21",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz",
@@ -15266,6 +15361,22 @@
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
"dev": true
},
"node_modules/node-fetch": {
"version": "3.0.0-beta.9",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0-beta.9.tgz",
"integrity": "sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==",
"dependencies": {
"data-uri-to-buffer": "^3.0.1",
"fetch-blob": "^2.1.1"
},
"engines": {
"node": "^10.17 || >=12.3"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/node-forge": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
@@ -15440,9 +15551,9 @@
}
},
"node_modules/nth-check": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
"integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
"dev": true,
"dependencies": {
"boolbase": "^1.0.0"
@@ -17870,9 +17981,9 @@
}
},
"node_modules/prismjs": {
"version": "1.24.1",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz",
"integrity": "sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow=="
"version": "1.25.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz",
"integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg=="
},
"node_modules/process": {
"version": "0.11.10",
@@ -19003,13 +19114,13 @@
}
},
"node_modules/refractor": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.4.0.tgz",
"integrity": "sha512-dBeD02lC5eytm9Gld2Mx0cMcnR+zhSnsTfPpWqFaMgUMJfC9A6bcN3Br/NaXrnBJcuxnLFR90k1jrkaSyV8umg==",
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.5.0.tgz",
"integrity": "sha512-QwPJd3ferTZ4cSPPjdP5bsYHMytwWYnAN5EEnLtGvkqp/FCCnGsBgxrm9EuIDnjUC3Uc/kETtvVi7fSIVC74Dg==",
"dependencies": {
"hastscript": "^6.0.0",
"parse-entities": "^2.0.0",
"prismjs": "~1.24.0"
"prismjs": "~1.25.0"
},
"funding": {
"type": "github",
@@ -21834,9 +21945,9 @@
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
"node_modules/tmpl": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
"integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
"integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
"dev": true
},
"node_modules/to-arraybuffer": {
@@ -22928,6 +23039,14 @@
"minimalistic-assert": "^1.0.0"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.1.tgz",
"integrity": "sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q==",
"engines": {
"node": ">= 8"
}
},
"node_modules/web-vitals": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-1.1.1.tgz",
@@ -26033,15 +26152,18 @@
}
},
"@ethersphere/bee-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-1.2.1.tgz",
"integrity": "sha512-Z1lh016EB8bxTNHUDXCDEcJN8IrPaz+vlIz89oscJF9q3Xuevmo/QCn7fQ0dqrppB8Motz7gjPteFJk5glsNGQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-2.1.0.tgz",
"integrity": "sha512-G33p+dGwSclIwG1dUKIfgACSHJouGh7LN2BOAeTg4Ts2Yg3Z/G+LzxCodyQpowLt0PaSTPjP2a/yER4NHoDUpg==",
"requires": {
"axios": "^0.21.1",
"cross-blob": "^2.0.1",
"elliptic": "^6.5.4",
"isomorphic-ws": "^4.0.1",
"js-sha3": "^0.8.0",
"ky": "^0.25.1",
"ky-universal": "^0.8.2",
"tar-js": "^0.3.0",
"web-streams-polyfill": "^3.1.0",
"ws": "^7.5.0"
}
},
@@ -27924,6 +28046,14 @@
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
"dev": true
},
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"requires": {
"event-target-shim": "^5.0.0"
}
},
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@@ -28077,9 +28207,9 @@
"dev": true
},
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true
},
"ansi-styles": {
@@ -28993,6 +29123,11 @@
"file-uri-to-path": "1.0.0"
}
},
"blob-polyfill": {
"version": "5.0.20210201",
"resolved": "https://registry.npmjs.org/blob-polyfill/-/blob-polyfill-5.0.20210201.tgz",
"integrity": "sha512-SrH6IG6aXL9pCgSysBCiDpGcAJ1j6/c1qCwR3sTEQJhb+MTk6FITNA6eW6WNYQDNZVi4Z9GjxH5v2MMTv59CrQ=="
},
"bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@@ -30072,6 +30207,15 @@
"sha.js": "^2.4.8"
}
},
"cross-blob": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/cross-blob/-/cross-blob-2.0.1.tgz",
"integrity": "sha512-ARuKPPo3I6DSqizal4UCyMCiGPQdMpMJS3Owx6Lleuh26vSt2UnfWRwbMLCYqbJUrcol+KzGVSLR91ezSHP80A==",
"requires": {
"blob-polyfill": "^5.0.20210201",
"fetch-blob": "^2.1.2"
}
},
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -30458,6 +30602,11 @@
"integrity": "sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw==",
"dev": true
},
"data-uri-to-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
"integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og=="
},
"data-urls": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
@@ -31793,6 +31942,11 @@
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"dev": true
},
"event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
},
"eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
@@ -32244,6 +32398,11 @@
"core-js": "^3.1.3"
}
},
"fetch-blob": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz",
"integrity": "sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow=="
},
"figgy-pudding": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
@@ -35838,6 +35997,20 @@
"integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==",
"dev": true
},
"ky": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/ky/-/ky-0.25.1.tgz",
"integrity": "sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA=="
},
"ky-universal": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.8.2.tgz",
"integrity": "sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==",
"requires": {
"abort-controller": "^3.0.0",
"node-fetch": "3.0.0-beta.9"
}
},
"language-subtag-registry": {
"version": "0.3.21",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz",
@@ -36545,6 +36718,15 @@
}
}
},
"node-fetch": {
"version": "3.0.0-beta.9",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0-beta.9.tgz",
"integrity": "sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==",
"requires": {
"data-uri-to-buffer": "^3.0.1",
"fetch-blob": "^2.1.1"
}
},
"node-forge": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
@@ -36688,9 +36870,9 @@
}
},
"nth-check": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
"integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
"dev": true,
"requires": {
"boolbase": "^1.0.0"
@@ -38645,9 +38827,9 @@
}
},
"prismjs": {
"version": "1.24.1",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz",
"integrity": "sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow=="
"version": "1.25.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz",
"integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg=="
},
"process": {
"version": "0.11.10",
@@ -39536,13 +39718,13 @@
}
},
"refractor": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.4.0.tgz",
"integrity": "sha512-dBeD02lC5eytm9Gld2Mx0cMcnR+zhSnsTfPpWqFaMgUMJfC9A6bcN3Br/NaXrnBJcuxnLFR90k1jrkaSyV8umg==",
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.5.0.tgz",
"integrity": "sha512-QwPJd3ferTZ4cSPPjdP5bsYHMytwWYnAN5EEnLtGvkqp/FCCnGsBgxrm9EuIDnjUC3Uc/kETtvVi7fSIVC74Dg==",
"requires": {
"hastscript": "^6.0.0",
"parse-entities": "^2.0.0",
"prismjs": "~1.24.0"
"prismjs": "~1.25.0"
}
},
"regenerate": {
@@ -41843,9 +42025,9 @@
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
"tmpl": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
"integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
"integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
"dev": true
},
"to-arraybuffer": {
@@ -42741,6 +42923,11 @@
"minimalistic-assert": "^1.0.0"
}
},
"web-streams-polyfill": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.1.tgz",
"integrity": "sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q=="
},
"web-vitals": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-1.1.1.tgz",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ethersphere/bee-dashboard",
"version": "0.7.0",
"version": "0.8.0",
"description": "An app which helps users to setup their Bee node and do actions like cash out cheques",
"keywords": [
"bee",
@@ -24,7 +24,7 @@
"url": "https://github.com/ethersphere/bee-dashboard.git"
},
"dependencies": {
"@ethersphere/bee-js": "1.2.1",
"@ethersphere/bee-js": "2.1.0",
"@material-ui/core": "4.12.3",
"@material-ui/icons": "4.11.2",
"@material-ui/lab": "4.0.0-alpha.57",
+3 -2
View File
@@ -7,6 +7,7 @@ import DialogContentText from '@material-ui/core/DialogContentText'
import DialogTitle from '@material-ui/core/DialogTitle'
import { useSnackbar } from 'notistack'
import { ReactElement, useState, useContext } from 'react'
import { Zap } from 'react-feather'
import { Context as SettingsContext } from '../providers/Settings'
import EthereumAddress from './EthereumAddress'
@@ -59,8 +60,8 @@ export default function CheckoutModal({ peerId, uncashedAmount }: Props): ReactE
return (
<div>
<Button variant="contained" color="primary" onClick={handleClickOpen} style={{ marginLeft: '7px' }}>
Cashout
<Button variant="contained" onClick={handleClickOpen} startIcon={<Zap size="1rem" />}>
Cash out peer {peerId.substr(0, 8)}[]
</Button>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Cashout Cheque</DialogTitle>
-50
View File
@@ -1,50 +0,0 @@
import React, { ReactElement, useState } from 'react'
import { TextField, Button } from '@material-ui/core'
interface Props {
defaultHost?: string
setHost: (host: string) => void
}
export default function ConnectToHost(props: Props): ReactElement {
const [hostInputVisible, toggleHostInputVisibility] = useState(false)
const [host, setHost] = useState('')
const handleNewHostConnection = () => {
if (host) {
props.setHost(host)
toggleHostInputVisibility(!hostInputVisible)
}
}
return (
<div>
{hostInputVisible ? (
<div style={{ display: 'flex' }}>
<TextField
defaultValue={props.defaultHost}
label="Enter host"
variant="outlined"
size="small"
onChange={e => setHost(e.target.value)}
style={{ marginRight: '15px', minWidth: '300px' }}
/>
<Button onClick={() => handleNewHostConnection()} size="small" variant="outlined">
Connect
</Button>
<Button
style={{ marginLeft: '7px' }}
onClick={() => toggleHostInputVisibility(!hostInputVisible)}
size="small"
>
Cancel
</Button>
</div>
) : (
<Button onClick={() => toggleHostInputVisibility(!hostInputVisible)} size="small" variant="outlined">
Change host
</Button>
)}
</div>
)
}
-59
View File
@@ -1,59 +0,0 @@
import { ReactElement } from 'react'
import { createStyles, makeStyles } from '@material-ui/core/styles'
import { Card, CardContent, Typography } from '@material-ui/core/'
import EthereumAddress from '../components/EthereumAddress'
import type { ChequebookAddressResponse, NodeAddresses } from '@ethersphere/bee-js'
const useStyles = makeStyles(() =>
createStyles({
root: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-around',
flexWrap: 'wrap',
},
details: {
display: 'flex',
flexDirection: 'column',
},
content: {
flex: '1 0 auto',
},
}),
)
interface Props {
nodeAddresses: NodeAddresses | null
chequebookAddress: ChequebookAddressResponse | null
}
function EthereumAddressCard(props: Props): ReactElement {
const classes = useStyles()
return (
<Card className={classes.root}>
<div className={classes.details}>
<CardContent className={classes.content}>
<Typography variant="subtitle1" gutterBottom>
Ethereum Address
</Typography>
<EthereumAddress address={props.nodeAddresses?.ethereum} />
</CardContent>
</div>
<div className={classes.details}>
<CardContent className={classes.content}>
<Typography variant="subtitle1" gutterBottom>
Chequebook Contract Address
</Typography>
<EthereumAddress address={props.chequebookAddress?.chequebookAddress} />
</CardContent>
</div>
</Card>
)
}
export default EthereumAddressCard
+82
View File
@@ -0,0 +1,82 @@
import { ReactElement, ReactNode, useState } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Collapse, ListItem, ListItemText, Typography } from '@material-ui/core'
import { ExpandLess, ExpandMore } from '@material-ui/icons'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
width: '100%',
padding: 0,
margin: 0,
marginTop: theme.spacing(4),
'&:first-child': {
marginTop: 0,
},
},
rootLevel1: { marginTop: theme.spacing(1) },
rootLevel2: { marginTop: theme.spacing(0.5) },
header: {
backgroundColor: theme.palette.background.paper,
},
contentLevel0: {
marginTop: theme.spacing(1),
},
contentLevel12: {
marginTop: theme.spacing(0.25),
},
infoText: {
color: '#c9c9c9',
},
}),
)
interface Props {
children?: ReactNode
label: ReactNode
info?: ReactNode
level?: 0 | 1 | 2
defaultOpen?: boolean
}
export default function ExpandableList({ children, label, level, defaultOpen, info }: Props): ReactElement | null {
const classes = useStyles()
const [open, setOpen] = useState<boolean>(Boolean(defaultOpen))
const handleClick = () => {
setOpen(!open)
}
let rootLevelClass = ''
let typographyVariant: 'h1' | 'h2' | 'h3' = 'h1'
let contentLevelClass = classes.contentLevel0
if (level === 1) {
rootLevelClass = classes.rootLevel1
typographyVariant = 'h2'
contentLevelClass = classes.contentLevel12
} else if (level === 2) {
rootLevelClass = classes.rootLevel2
typographyVariant = 'h3'
contentLevelClass = classes.contentLevel12
}
return (
<div className={`${classes.root} ${rootLevelClass}`}>
<ListItem button onClick={handleClick} className={classes.header}>
<ListItemText primary={<Typography variant={typographyVariant}>{label}</Typography>} />
<div style={{ display: 'flex' }}>
{!open && (
<Typography variant="body2" className={classes.infoText}>
{info}
</Typography>
)}
{open ? <ExpandLess /> : <ExpandMore />}
</div>
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<div className={contentLevelClass}>{children}</div>
</Collapse>
</div>
)
}
+54
View File
@@ -0,0 +1,54 @@
import { ReactElement, ReactNode } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Typography, Grid, IconButton, Tooltip } from '@material-ui/core'
import { Info } from 'react-feather'
import ListItem from '@material-ui/core/ListItem'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
header: {
backgroundColor: theme.palette.background.paper,
marginBottom: theme.spacing(0.25),
wordBreak: 'break-word',
},
copyValue: {
cursor: 'pointer',
padding: theme.spacing(1),
borderRadius: 0,
'&:hover': {
backgroundColor: '#fcf2e8',
color: theme.palette.primary.main,
},
},
}),
)
interface Props {
label?: ReactNode
value?: ReactNode
tooltip?: string
}
export default function ExpandableListItem({ label, value, tooltip }: Props): ReactElement | null {
const classes = useStyles()
return (
<ListItem className={classes.header}>
<Grid container direction="row" justifyContent="space-between" alignItems="center">
{label && <Typography variant="body1">{label}</Typography>}
{value && (
<Typography variant="body2">
{value}
{tooltip && (
<Tooltip title={tooltip} placement="top" arrow>
<IconButton size="small" className={classes.copyValue}>
<Info strokeWidth={1} />
</IconButton>
</Tooltip>
)}
</Typography>
)}
</Grid>
</ListItem>
)
}
@@ -0,0 +1,39 @@
import { ReactElement, ReactNode } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Grid } from '@material-ui/core'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
action: {
marginTop: theme.spacing(0.75),
marginBottom: theme.spacing(1),
marginRight: theme.spacing(1),
},
}),
)
interface Props {
children: ReactNode | ReactNode[]
}
export default function ExpandableListItemActions({ children }: Props): ReactElement | null {
const classes = useStyles()
if (Array.isArray(children)) {
return (
<Grid container direction="row">
{children.map((a, i) => (
<Grid key={i} className={classes.action}>
{a}
</Grid>
))}
</Grid>
)
}
return (
<Grid container direction="row">
<Grid className={classes.action}>{children}</Grid>
</Grid>
)
}
+145
View File
@@ -0,0 +1,145 @@
import { ReactElement, ChangeEvent, useState } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import Collapse from '@material-ui/core/Collapse'
import { ListItem, Typography, Grid, IconButton, InputBase, Button } from '@material-ui/core'
import { Edit, Minus, RotateCcw, Check } from 'react-feather'
import ExpandableListItemActions from './ExpandableListItemActions'
import ExpandableListItemNote from './ExpandableListItemNote'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
header: {
backgroundColor: theme.palette.background.paper,
marginBottom: theme.spacing(0.25),
borderLeft: `${theme.spacing(0.25)}px solid rgba(0,0,0,0)`,
wordBreak: 'break-word',
},
headerOpen: {
borderLeft: `${theme.spacing(0.25)}px solid ${theme.palette.primary.main}`,
},
copyValue: {
cursor: 'pointer',
padding: theme.spacing(1),
borderRadius: 0,
'&:hover': {
backgroundColor: '#fcf2e8',
color: theme.palette.primary.main,
},
},
content: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
},
keyMargin: {
marginRight: theme.spacing(1),
},
unselectableLabel: {
cursor: 'default',
userSelect: 'none',
// Many browsers don't support yet the general user-select css property
WebkitUserSelect: 'none',
MozUserSelect: 'none',
msUserSelect: 'none',
},
}),
)
interface Props {
label: string
value?: string
placeholder?: string
helperText?: string
expandedOnly?: boolean
confirmLabel?: string
confirmLabelDisabled?: boolean
onChange?: (value: string) => void
onConfirm: (value: string) => void
}
export default function ExpandableListItemKey({
label,
value,
onConfirm,
onChange,
confirmLabel,
confirmLabelDisabled,
expandedOnly,
helperText,
placeholder,
}: Props): ReactElement | null {
const classes = useStyles()
const [open, setOpen] = useState(Boolean(expandedOnly))
const [inputValue, setInputValue] = useState<string>(value || '')
const toggleOpen = () => setOpen(!open)
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
setInputValue(e.target.value)
if (onChange) onChange(e.target.value)
}
return (
<>
<ListItem className={`${classes.header} ${open ? classes.headerOpen : ''}`}>
<Grid container direction="column" justifyContent="space-between" alignItems="stretch">
<Grid container direction="row" justifyContent="space-between" alignItems="center">
{label && (
<Typography variant="body1" className={classes.unselectableLabel}>
{label}
</Typography>
)}
<Typography variant="body2">
<div>
{!open && value}
{!expandedOnly && (
<IconButton size="small" className={classes.copyValue}>
{open ? (
<Minus onClick={toggleOpen} strokeWidth={1} />
) : (
<Edit onClick={toggleOpen} strokeWidth={1} />
)}
</IconButton>
)}
</div>
</Typography>
</Grid>
<Collapse in={open} timeout="auto" unmountOnExit>
<InputBase
value={inputValue}
placeholder={placeholder}
onChange={handleChange}
fullWidth
className={classes.content}
autoFocus
/>
</Collapse>
</Grid>
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
{helperText && <ExpandableListItemNote>{helperText}</ExpandableListItemNote>}
<ExpandableListItemActions>
<Button
variant="contained"
disabled={
inputValue === value ||
Boolean(confirmLabelDisabled) || // Disable if external validation is provided
(inputValue === '' && value === undefined) // Disable if no initial value was not provided and the field is empty. The undefined check is improtant so that it is possible to submit with empty input in other cases
}
startIcon={<Check size="1rem" />}
onClick={() => onConfirm(inputValue)}
>
{confirmLabel || 'Save'}
</Button>
<Button
variant="contained"
disabled={inputValue === value || inputValue === ''}
startIcon={<RotateCcw size="1rem" />}
onClick={() => setInputValue(value || '')}
>
Cancel
</Button>
</ExpandableListItemActions>
</Collapse>
</>
)
}
+114
View File
@@ -0,0 +1,114 @@
import { ReactElement, useState } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import Collapse from '@material-ui/core/Collapse'
import { ListItem, Typography, Grid, IconButton, Tooltip } from '@material-ui/core'
import { Eye, Minus } from 'react-feather'
import { CopyToClipboard } from 'react-copy-to-clipboard'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
header: {
backgroundColor: theme.palette.background.paper,
marginBottom: theme.spacing(0.25),
borderLeft: `${theme.spacing(0.25)}px solid rgba(0,0,0,0)`,
wordBreak: 'break-word',
},
headerOpen: {
borderLeft: `${theme.spacing(0.25)}px solid ${theme.palette.primary.main}`,
},
copyValue: {
cursor: 'pointer',
padding: theme.spacing(1),
borderRadius: 0,
'&:hover': {
backgroundColor: '#fcf2e8',
color: theme.palette.primary.main,
},
},
content: {
marginTop: theme.spacing(2),
marginBottom: theme.spacing(2),
},
keyMargin: {
marginRight: theme.spacing(1),
},
}),
)
interface Props {
label: string
value: string
}
const lengthWithoutPrefix = (s: string) => s.replace(/^0x/i, '').length
function isPrefixedHexString(s: unknown): boolean {
return typeof s === 'string' && /^0x[0-9a-f]+$/i.test(s)
}
const split = (s: string): string[] => {
const nonPrefixLength = lengthWithoutPrefix(s)
if (nonPrefixLength % 6 === 0) return s.match(/(0x|.{6})/gi) || []
return s.match(/(0x|.{1,8})/gi) || []
}
export default function ExpandableListItemKey({ label, value }: Props): ReactElement | null {
const classes = useStyles()
const [open, setOpen] = useState(false)
const [copied, setCopied] = useState(false)
const toggleOpen = () => setOpen(!open)
const tooltipClickHandler = () => setCopied(true)
const tooltipCloseHandler = () => setCopied(false)
const splitValues = split(value)
const hasPrefix = isPrefixedHexString(value)
return (
<ListItem className={`${classes.header} ${open ? classes.headerOpen : ''}`}>
<Grid container direction="column" justifyContent="space-between" alignItems="stretch">
<Grid container direction="row" justifyContent="space-between" alignItems="center">
{label && <Typography variant="body1">{label}</Typography>}
<Typography variant="body2">
<div>
{!open && (
<span className={classes.copyValue}>
<Tooltip title={copied ? 'Copied' : 'Copy'} placement="top" arrow onClose={tooltipCloseHandler}>
<CopyToClipboard text={value}>
<span onClick={tooltipClickHandler}>{`${
hasPrefix ? `${splitValues[0]} ${splitValues[1]}` : splitValues[0]
}[…]${splitValues[splitValues.length - 1]}`}</span>
</CopyToClipboard>
</Tooltip>
</span>
)}
<IconButton size="small" className={classes.copyValue}>
{open ? <Minus onClick={toggleOpen} strokeWidth={1} /> : <Eye onClick={toggleOpen} strokeWidth={1} />}
</IconButton>
</div>
</Typography>
</Grid>
<Collapse in={open} timeout="auto" unmountOnExit>
<div className={classes.content}>
<Tooltip title={copied ? 'Copied' : 'Copy'} placement="top" arrow onClose={tooltipCloseHandler}>
<CopyToClipboard text={value}>
{/* This has to be wrapped in two spans otherwise either the tooltip or the highlighting does not work*/}
<span onClick={tooltipClickHandler}>
<span className={classes.copyValue}>
{splitValues.map((s, i) => (
<Typography variant="body2" key={i} className={classes.keyMargin} component="span">
{s}
</Typography>
))}
</span>
</span>
</CopyToClipboard>
</Tooltip>
</div>
</Collapse>
</Grid>
</ListItem>
)
}
+32
View File
@@ -0,0 +1,32 @@
import { ReactElement, ReactNode } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Typography } from '@material-ui/core'
import ListItem from '@material-ui/core/ListItem'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
header: {
backgroundColor: '#F7F7F7',
marginBottom: theme.spacing(0.25),
},
typography: {
color: '#242424',
},
}),
)
interface Props {
children?: ReactNode | ReactNode[]
}
export default function ExpandableListItemNote({ children }: Props): ReactElement | null {
const classes = useStyles()
return (
<ListItem className={classes.header}>
<Typography variant="body1" className={classes.typography}>
{children}
</Typography>
</ListItem>
)
}
-24
View File
@@ -1,24 +0,0 @@
import type { ReactElement } from 'react'
import { Typography } from '@material-ui/core'
function truncStringPortion(str: string, firstCharCount = 10, endCharCount = 10) {
return `${str.substring(0, firstCharCount)}...${str.substring(str.length - endCharCount, str.length)}`
}
interface Props {
peerId: string
characterLength?: number
}
export default function PeerDetail({ peerId, characterLength }: Props): ReactElement {
return (
<Typography
variant="button"
style={{
fontFamily: 'monospace, monospace',
}}
>
{truncStringPortion(peerId, characterLength, characterLength)}
</Typography>
)
}
+12 -7
View File
@@ -4,7 +4,7 @@ import { Link } from 'react-router-dom'
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'
import { OpenInNewSharp } from '@material-ui/icons'
import { Divider, List, Drawer, Grid, Link as MUILink } from '@material-ui/core'
import { Home, FileText, DollarSign, Share2, Settings, Layers, BookOpen } from 'react-feather'
import { Home, FileText, DollarSign, Settings, Layers, BookOpen } from 'react-feather'
import { ROUTES } from '../routes'
import SideBarItem from './SideBarItem'
import SideBarStatus from './SideBarStatus'
@@ -32,11 +32,6 @@ const navBarItems = [
path: ROUTES.ACCOUNTING,
icon: DollarSign,
},
{
label: 'Peers',
path: ROUTES.PEERS,
icon: Share2,
},
{
label: 'Settings',
path: ROUTES.SETTINGS,
@@ -44,6 +39,8 @@ const navBarItems = [
},
]
const drawerWidth = 300
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
@@ -52,6 +49,14 @@ const useStyles = makeStyles((theme: Theme) =>
paddingTop: theme.spacing(8),
paddingBottom: theme.spacing(8),
},
drawer: {
width: drawerWidth,
flexShrink: 0,
},
drawerPaper: {
width: drawerWidth,
backgroundColor: '#212121',
},
logo: {
marginLeft: theme.spacing(8),
marginRight: theme.spacing(8),
@@ -86,7 +91,7 @@ export default function SideBar(): ReactElement {
const classes = useStyles()
return (
<Drawer variant="permanent">
<Drawer className={classes.drawer} variant="permanent" anchor="left" classes={{ paper: classes.drawerPaper }}>
<Grid container direction="column" justifyContent="space-between" className={classes.root}>
<Grid className={classes.logo}>
<Link to={ROUTES.INFO}>
+6 -12
View File
@@ -5,6 +5,7 @@ import { ArrowRight } from 'react-feather'
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'
import { ListItemText, ListItemIcon, ListItem, Typography } from '@material-ui/core'
import { Context } from '../providers/Bee'
import StatusIcon from './StatusIcon'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
@@ -32,10 +33,12 @@ const useStyles = makeStyles((theme: Theme) =>
button: {
'&:hover': {
backgroundColor: '#2c2c2c',
color: 'white',
// https://github.com/mui-org/material-ui/issues/22543
'@media (hover: none)': {
backgroundColor: '#2c2c2c',
color: 'white',
},
},
},
@@ -50,7 +53,7 @@ interface Props {
}
export default function SideBarItem({ path }: Props): ReactElement {
const { status } = useContext(Context)
const { status, isLoading } = useContext(Context)
const classes = useStyles()
const location = useLocation()
const isSelected = Boolean(matchPath(location.pathname, { path, exact: true }))
@@ -62,17 +65,8 @@ export default function SideBarItem({ path }: Props): ReactElement {
selected={isSelected}
disableRipple
>
<ListItemIcon>
<span
style={{
backgroundColor: status.all ? '#1de600' : '#ff3a52',
height: '14px',
width: '14px',
borderRadius: '50%',
display: 'inline-block',
marginLeft: 30,
}}
/>
<ListItemIcon style={{ marginLeft: '30px' }}>
<StatusIcon isOk={status.all} isLoading={isLoading} />
</ListItemIcon>
<ListItemText
primary={<Typography className={classes.smallerText}>{`Node ${status.all ? 'OK' : 'Error'}`}</Typography>}
-43
View File
@@ -1,43 +0,0 @@
import { Card, CardContent, Typography } from '@material-ui/core/'
import { makeStyles } from '@material-ui/core/styles'
import { Skeleton } from '@material-ui/lab'
import type { ReactElement } from 'react'
import { Title } from './Title'
const useStyles = makeStyles({
root: {
minWidth: 275,
},
})
interface Props {
label: string
statistic?: string
loading?: boolean
tooltip?: string
}
export default function StatCard({ loading, label, statistic, tooltip }: Props): ReactElement {
const classes = useStyles()
return (
<Card className={classes.root}>
<CardContent>
{loading && (
<>
<Skeleton width={180} height={25} animation="wave" />
<Skeleton width={180} height={35} animation="wave" />
</>
)}
{!loading && (
<>
<Title label={label} tooltip={tooltip} />
<Typography variant="h5" component="h2">
{statistic}
</Typography>
</>
)}
</CardContent>
</Card>
)
}
+28
View File
@@ -0,0 +1,28 @@
import type { ReactElement } from 'react'
import { CircularProgress } from '@material-ui/core'
interface Props {
isOk: boolean
isLoading?: boolean
size?: number | string
className?: string
}
export default function StatusIcon({ isOk, size, className, isLoading }: Props): ReactElement {
const s = size || '1rem'
if (isLoading) return <CircularProgress size={s} className={className} />
return (
<span
className={className}
style={{
backgroundColor: isOk ? '#1de600' : '#ff3a52',
height: s,
width: s,
borderRadius: '50%',
display: 'inline-block',
}}
/>
)
}
+14 -14
View File
@@ -1,9 +1,6 @@
import React, { ReactElement, ReactNode } from 'react'
import { makeStyles } from '@material-ui/core/styles'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'
import Typography from '@material-ui/core/Typography'
import Box from '@material-ui/core/Box'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Tab, Tabs } from '@material-ui/core'
interface TabPanelProps {
children?: ReactNode
@@ -16,24 +13,25 @@ function TabPanel(props: TabPanelProps) {
return (
<div role="tabpanel" hidden={value !== index} {...other}>
{value === index && (
<Box p={3}>
<Typography>{children}</Typography>
</Box>
)}
{value === index && children}
</div>
)
}
const useStyles = makeStyles(() => ({
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
flexGrow: 1,
},
}))
content: {
marginTop: theme.spacing(2),
},
}),
)
interface TabsValues {
component: ReactNode
label: string
label: ReactNode
}
interface Props {
@@ -55,16 +53,18 @@ export default function SimpleTabs({ values, index, indexChanged }: Props): Reac
return (
<div className={classes.root}>
<Tabs value={v} onChange={handleChange}>
<Tabs value={v} onChange={handleChange} variant="fullWidth">
{values.map(({ label }, idx) => (
<Tab key={idx} label={label} />
))}
</Tabs>
<div className={classes.content}>
{values.map(({ component }, idx) => (
<TabPanel key={idx} value={v} index={idx}>
{component}
</TabPanel>
))}
</div>
</div>
)
}
-41
View File
@@ -1,41 +0,0 @@
import { Grid, Tooltip, Typography } from '@material-ui/core/'
import { makeStyles } from '@material-ui/core/styles'
import { Info } from '@material-ui/icons'
import type { ReactElement } from 'react'
interface TitleProps {
label: string
tooltip?: string
}
const useStyles = makeStyles({
title: {
fontSize: 16,
},
})
export function Title({ label, tooltip }: TitleProps): ReactElement {
const classes = useStyles()
if (!tooltip) {
return (
<Typography className={classes.title} color="textSecondary" gutterBottom>
{label}
</Typography>
)
}
// span is needed as Tooltip expects a non-functional element!
return (
<Tooltip title={tooltip}>
<span>
<Grid container direction="row" justify="space-between">
<Typography className={classes.title} color="textSecondary" gutterBottom>
{label}
</Typography>
<Info />
</Grid>
</span>
</Tooltip>
)
}
+21 -44
View File
@@ -1,66 +1,43 @@
import type { Topology } from '@ethersphere/bee-js'
import { Grid } from '@material-ui/core/'
import type { ReactElement } from 'react'
import { pickThreshold, ThresholdValues } from '../utils/threshold'
import StatCard from './StatCard'
import ExpandableListItem from './ExpandableListItem'
interface RootProps {
interface Props {
topology: Topology | null
}
interface Props extends RootProps {
thresholds: ThresholdValues
}
const TopologyStats = (props: RootProps): ReactElement => {
const TopologyStats = (props: Props): ReactElement => {
const thresholds: ThresholdValues = {
connectedPeers: pickThreshold('connectedPeers', props.topology?.connected || 0),
population: pickThreshold('population', props.topology?.population || 0),
depth: pickThreshold('depth', props.topology?.depth || 0),
}
return (
<>
<Indicator {...props} thresholds={thresholds} />
<Metrics {...props} thresholds={thresholds} />
</>
)
}
const Indicator = ({ thresholds }: Props): ReactElement => {
const maximumTotalScore = Object.values(thresholds).reduce((sum, item) => sum + item.maximumScore, 0)
const actualTotalScore = Object.values(thresholds).reduce((sum, item) => sum + item.score, 0)
const percentageText = Math.round((actualTotalScore / maximumTotalScore) * 100) + '%'
return (
<div style={{ marginBottom: '20px' }}>
<StatCard label="Overall Health Indicator" statistic={percentageText} />
</div>
<>
<ExpandableListItem label="Overall Health Indicator" value={percentageText} />
<ExpandableListItem
label="Connected Peers"
value={props.topology?.connected.toString()}
tooltip={thresholds.connectedPeers.explanation}
/>
<ExpandableListItem
label="Population"
value={props.topology?.population.toString()}
tooltip={thresholds.population.explanation}
/>
<ExpandableListItem
label="Depth"
value={props.topology?.depth.toString()}
tooltip={thresholds.depth.explanation}
/>
</>
)
}
const Metrics = ({ topology, thresholds }: Props): ReactElement => (
<Grid style={{ marginBottom: '20px', flexGrow: 1 }}>
<Grid container spacing={3}>
<Grid key={1} item xs={12} sm={12} md={6} lg={4} xl={4}>
<StatCard
label="Connected Peers"
statistic={topology?.connected.toString()}
tooltip={thresholds.connectedPeers.explanation}
/>
</Grid>
<Grid key={2} item xs={12} sm={12} md={6} lg={4} xl={4}>
<StatCard
label="Population"
statistic={topology?.population.toString()}
tooltip={thresholds.population.explanation}
/>
</Grid>
<Grid key={3} item xs={12} sm={12} md={6} lg={4} xl={4}>
<StatCard label="Depth" statistic={topology?.depth.toString()} tooltip={thresholds.depth.explanation} />
</Grid>
</Grid>
</Grid>
)
export default TopologyStats
+47 -31
View File
@@ -1,48 +1,64 @@
import type { ReactElement } from 'react'
import { Link } from 'react-router-dom'
import { makeStyles } from '@material-ui/core/styles'
import { Card, CardContent, Typography } from '@material-ui/core/'
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
import { Button, Grid, Typography, Link as MuiLink } from '@material-ui/core/'
import { ROUTES } from '../routes'
import { Activity } from 'react-feather'
const useStyles = makeStyles({
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
flexGrow: 1,
marginTop: '20px',
height: '100%',
},
title: {
textAlign: 'center',
fontSize: 26,
content: {
maxWidth: 500,
marginBottom: theme.spacing(4),
'&:last-child': {
marginBottom: 0,
},
})
},
icon: {
height: '1rem',
},
}),
)
export default function TroubleshootConnectionCard(): ReactElement {
const classes = useStyles()
return (
<Card className={classes.root}>
<CardContent>
<Typography className={classes.title} gutterBottom>
Looks like your node is not connected
<Grid container direction="column" justifyContent="center" alignItems="center" className={classes.root}>
<Grid item className={classes.content}>
<Typography variant="h1" align="center">
Uh oh, it looks like your node is not connected.
</Typography>
<div style={{ marginBottom: '20px', textAlign: 'center' }}>
<strong>
<Link to={ROUTES.STATUS}>Click to run status checks</Link> on your nodes connection or check out the{' '}
<a href={process.env.REACT_APP_BEE_DOCS_HOST} target="_blank" rel="noreferrer">
</Grid>
<Grid item className={classes.content}>
<Typography align="center">
Please check your node status to fix the problem. You can also check out the{' '}
<MuiLink href={process.env.REACT_APP_BEE_DOCS_HOST} target="_blank" rel="noreferrer">
Swarm Bee Docs
</a>
</strong>
</div>
<div style={{ marginBottom: '20px', textAlign: 'center' }}>
<p style={{ marginTop: '50px' }}>
Still not working? Drop us a message on the Ethereum Swarm{' '}
<a href={process.env.REACT_APP_BEE_DISCORD_HOST} target="_blank" rel="noreferrer">
Discord
</a>
</p>
</div>
</CardContent>
</Card>
</MuiLink>{' '}
or ask for support on the{' '}
<MuiLink href={process.env.REACT_APP_BEE_DISCORD_HOST} target="_blank" rel="noreferrer">
Ethereum Swarm Discord
</MuiLink>
.
</Typography>
</Grid>
<Grid item className={classes.content}>
<Typography align="center">
<Button
component={Link}
variant="contained"
startIcon={<Activity className={classes.icon} />}
to={ROUTES.STATUS}
>
Check node status
</Button>
</Typography>
</Grid>
</Grid>
)
}
+6 -3
View File
@@ -1,4 +1,4 @@
import { ReactElement, useState } from 'react'
import { ReactElement, ReactNode, useState } from 'react'
import Button from '@material-ui/core/Button'
import Input from '@material-ui/core/Input'
import Dialog from '@material-ui/core/Dialog'
@@ -19,6 +19,7 @@ interface Props {
max?: BigNumber
min?: BigNumber
action: (amount: bigint) => Promise<string>
icon?: ReactNode
}
export default function WithdrawDepositModal({
@@ -29,6 +30,7 @@ export default function WithdrawDepositModal({
max,
label,
action,
icon,
}: Props): ReactElement {
const [open, setOpen] = useState(false)
const [amount, setAmount] = useState('')
@@ -36,8 +38,9 @@ export default function WithdrawDepositModal({
const [amountError, setAmountError] = useState<Error | null>(null)
const { enqueueSnackbar } = useSnackbar()
const handleClickOpen = () => {
const handleClickOpen = (e: React.MouseEvent<HTMLButtonElement>) => {
setOpen(true)
e.stopPropagation()
}
const handleClose = () => {
@@ -74,7 +77,7 @@ export default function WithdrawDepositModal({
return (
<div>
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
<Button variant="contained" onClick={handleClickOpen} startIcon={icon}>
{label}
</Button>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
+3 -1
View File
@@ -1,4 +1,5 @@
import { ReactElement, useContext } from 'react'
import { Download } from 'react-feather'
import { Context as SettingsContext } from '../providers/Settings'
import WithdrawDepositModal from '../components/WithdrawDepositModal'
@@ -11,8 +12,9 @@ export default function DepositModal(): ReactElement {
<WithdrawDepositModal
successMessage="Successful deposit."
errorMessage="Error with depositing"
dialogMessage="Specify the amount of BZZ you would like to withdraw from your node."
dialogMessage="Specify the amount of BZZ you would like to deposit to your node."
label="Deposit"
icon={<Download size="1rem" />}
min={new BigNumber(0)}
action={(amount: bigint) => {
if (!beeDebugApi) throw new Error('Bee Debug URL is not valid')
+2
View File
@@ -1,4 +1,5 @@
import { ReactElement, useContext } from 'react'
import { Upload } from 'react-feather'
import { Context as SettingsContext } from '../providers/Settings'
import WithdrawDepositModal from '../components/WithdrawDepositModal'
@@ -13,6 +14,7 @@ export default function WithdrawModal(): ReactElement {
errorMessage="Error with withdrawing."
dialogMessage="Specify the amount of BZZ you would like to withdraw from your node."
label="Withdraw"
icon={<Upload size="1rem" />}
min={new BigNumber(0)}
action={(amount: bigint) => {
if (!beeDebugApi) throw new Error('Bee Debug URL is not valid')
+17 -5
View File
@@ -6,6 +6,7 @@ import { Balance, Settlements, Settlement } from '../types'
interface UseAccountingHook {
isLoadingUncashed: boolean
totalUncashed: Token
accounting: Accounting[] | null
}
@@ -60,16 +61,21 @@ function mergeAccounting(
}),
)
// If there are no cheques (and hence last cashout actions), we don't need to sort and can return values right away
if (!uncashedAmounts) return Object.values(accounting)
// If there are no cheques (and hence last cashout actions)
if (!uncashedAmounts) return Object.values(accounting).sort((a, b) => (a.peer < b.peer ? -1 : 1))
uncashedAmounts?.forEach(({ peer, uncashedAmount }) => {
accounting[peer].uncashedAmount = new Token(uncashedAmount)
})
return Object.values(accounting).sort((a, b) =>
b.uncashedAmount.toBigNumber.minus(a.uncashedAmount.toBigNumber).toNumber(),
)
// Return sorted by the uncashed amount first and then by the peer id
return Object.values(accounting).sort((a, b) => {
const diff = b.uncashedAmount.toBigNumber.minus(a.uncashedAmount.toBigNumber).toNumber()
if (diff !== 0) return diff
return a.peer < b.peer ? -1 : 1
})
}
export const useAccounting = (
@@ -98,8 +104,14 @@ export const useAccounting = (
const accounting = mergeAccounting(balances, settlements?.settlements, uncashedAmounts)
let totalUncashed: Token = new Token('0')
accounting?.forEach(
({ uncashedAmount }) => (totalUncashed = new Token(totalUncashed.toBigNumber.plus(uncashedAmount.toBigNumber))),
)
return {
isLoadingUncashed,
totalUncashed,
accounting,
}
}
+8 -9
View File
@@ -12,11 +12,8 @@ import { Context } from '../providers/Bee'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
content: {
marginLeft: 300,
flexGrow: 1,
backgroundColor: theme.palette.background.default,
padding: theme.spacing(3),
paddingBottom: '65px',
minHeight: '100vh',
},
}),
)
@@ -31,20 +28,22 @@ const Dashboard = (props: Props): ReactElement => {
const { isLoading } = useContext(Context)
return (
<div>
<div style={{ display: 'flex' }}>
<SideBar />
<Container className={classes.content}>
<ErrorBoundary>
<main className={classes.content}>
<>
<AlertVersion />
{isLoading ? (
<Container style={{ textAlign: 'center', padding: '50px' }}>
<div style={{ textAlign: 'center', width: '100%' }}>
<CircularProgress />
</Container>
</div>
) : (
props.children
)}
</main>
</>
</ErrorBoundary>
</Container>
</div>
)
}
-94
View File
@@ -1,94 +0,0 @@
import { ReactElement } from 'react'
import { createStyles, makeStyles } from '@material-ui/core/styles'
import { Card, CardContent, Typography, Theme } from '@material-ui/core/'
import WithdrawModal from '../../containers/WithdrawModal'
import DepositModal from '../../containers/DepositModal'
import type { ChequebookAddressResponse } from '@ethersphere/bee-js'
import { Token } from '../../models/Token'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
display: 'flex',
},
buttons: {
display: 'flex',
columnGap: theme.spacing(1),
},
gridContainer: {
display: 'flex',
width: '100%',
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
columnGap: theme.spacing(1),
rowGap: theme.spacing(1),
flex: '0 1 auto',
flexWrap: 'wrap',
justifyContent: 'space-between',
},
chequebookActions: {
justifyContent: 'space-between',
display: 'flex',
marginBottom: theme.spacing(1),
},
}),
)
interface ChequebookBalance {
totalBalance: Token
availableBalance: Token
}
interface Props {
chequebookAddress: ChequebookAddressResponse | null
chequebookBalance: ChequebookBalance | null
totalsent?: Token
totalreceived?: Token
}
function AccountCard({ totalreceived, totalsent, chequebookBalance }: Props): ReactElement {
const classes = useStyles()
return (
<div>
<div className={classes.chequebookActions}>
<Typography component="h2" variant="h6">
Chequebook
</Typography>
<div className={classes.buttons}>
<WithdrawModal />
<DepositModal />
</div>
</div>
<Card className={classes.root}>
<CardContent className={classes.gridContainer}>
<div>
<Typography component="h2" variant="h6" color="primary" gutterBottom>
Total Balance
</Typography>
<Typography variant="h5">{chequebookBalance?.totalBalance.toFixedDecimal()} BZZ</Typography>
</div>
<div>
<Typography component="h2" variant="h6" color="primary" gutterBottom>
Available Uncommitted Balance
</Typography>
<Typography variant="h5">{chequebookBalance?.availableBalance.toFixedDecimal()} BZZ</Typography>
</div>
<div>
<Typography component="h2" variant="h6" color="primary" gutterBottom>
Total Sent / Received
</Typography>
<Typography variant="h5">
{totalsent?.toFixedDecimal()} / {totalreceived?.toFixedDecimal()} BZZ
</Typography>
</div>
</CardContent>
</Card>
</div>
)
}
export default AccountCard
-94
View File
@@ -1,94 +0,0 @@
import type { ReactElement } from 'react'
import { makeStyles } from '@material-ui/core/styles'
import { Table, TableBody, TableCell, TableContainer, TableRow, TableHead, Paper } from '@material-ui/core'
import ClipboardCopy from '../../components/ClipboardCopy'
import CashoutModal from '../../components/CashoutModal'
import PeerDetailDrawer from '../../components/PeerDetail'
import { Accounting } from '../../hooks/accounting'
const useStyles = makeStyles({
table: {
minWidth: 650,
},
values: {
textAlign: 'right',
fontFamily: 'monospace, monospace',
},
})
interface Props {
isLoadingUncashed: boolean
accounting: Accounting[] | null
}
function BalancesTable({ accounting, isLoadingUncashed }: Props): ReactElement | null {
if (accounting === null) return null
const classes = useStyles()
return (
<TableContainer component={Paper}>
<Table className={classes.table} size="small" aria-label="Balances Table">
<TableHead>
<TableRow>
<TableCell>Peer</TableCell>
<TableCell align="right">Outstanding Balance</TableCell>
<TableCell align="right">Settlements Sent / Received</TableCell>
<TableCell align="right">Total</TableCell>
<TableCell align="right">Uncashed Amount</TableCell>
<TableCell />
</TableRow>
</TableHead>
<TableBody>
{accounting.map(({ peer, balance, received, sent, uncashedAmount, total }) => (
<TableRow key={peer}>
<TableCell>
<div style={{ display: 'flex' }}>
<small>
<PeerDetailDrawer peerId={peer} />
</small>
<ClipboardCopy value={peer} />
</div>
</TableCell>
<TableCell className={classes.values}>
<span
style={{
color: balance.toBigNumber.isPositive() ? '#32c48d' : '#c9201f',
}}
>
{balance.toFixedDecimal()}
</span>{' '}
BZZ
</TableCell>
<TableCell className={classes.values}>
-{sent.toFixedDecimal()} / {received.toFixedDecimal()} BZZ
</TableCell>
<TableCell className={classes.values}>
<span
style={{
color: total.toBigNumber.isPositive() ? '#32c48d' : '#c9201f',
}}
>
{total.toFixedDecimal()}
</span>{' '}
BZZ
</TableCell>
<TableCell className={classes.values}>
{isLoadingUncashed && 'loading...'}
{!isLoadingUncashed && (
<>{uncashedAmount.toBigNumber.isGreaterThan('0') ? uncashedAmount.toFixedDecimal() : '0'} BZZ</>
)}
</TableCell>
<TableCell className={classes.values}>
{uncashedAmount.toBigNumber.isGreaterThan('0') && (
<CashoutModal uncashedAmount={uncashedAmount.toFixedDecimal()} peerId={peer} />
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)
}
export default BalancesTable
+52
View File
@@ -0,0 +1,52 @@
import type { ReactElement } from 'react'
import ExpandableList from '../../components/ExpandableList'
import ExpandableListItem from '../../components/ExpandableListItem'
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
import CashoutModal from '../../components/CashoutModal'
import { Accounting } from '../../hooks/accounting'
import type { Token } from '../../models/Token'
interface Props {
isLoadingUncashed: boolean
totalUncashed: Token
accounting: Accounting[] | null
}
export default function PeerBalances({ accounting, isLoadingUncashed, totalUncashed }: Props): ReactElement | null {
return (
<ExpandableList
label={`Peers (${accounting?.length || 0})`}
info={`${totalUncashed.toFixedDecimal()} BZZ (uncashed)`}
>
<ExpandableListItem label="Uncashed Amount Total" value={`${totalUncashed.toFixedDecimal()} BZZ`} />
{accounting?.map(({ peer, balance, received, sent, uncashedAmount, total }) => (
<ExpandableList
key={peer}
label={`Peer ${peer.substr(0, 8)}[…]`}
level={1}
info={`${uncashedAmount.toFixedDecimal()} BZZ (uncashed)`}
>
<ExpandableListItemKey label="Peer ID" value={peer} />
<ExpandableListItem label="Outstanding Balance" value={`${balance.toFixedDecimal()} BZZ`} />
<ExpandableListItem
label="Settlements Sent / Received"
value={`-${sent.toFixedDecimal()} / ${received.toFixedDecimal()} BZZ`}
/>
<ExpandableListItem label="Total" value={`${total.toFixedDecimal()} BZZ`} />
<ExpandableListItem
label="Uncashed Amount"
value={isLoadingUncashed ? 'loading…' : `${uncashedAmount.toFixedDecimal()} BZZ`}
/>
{uncashedAmount.toBigNumber.isGreaterThan('0') && (
<ExpandableListItemActions>
<CashoutModal uncashedAmount={uncashedAmount.toFixedDecimal()} peerId={peer} />
</ExpandableListItemActions>
)}
</ExpandableList>
))}
</ExpandableList>
)
}
+32 -25
View File
@@ -1,45 +1,52 @@
import { ReactElement, useContext } from 'react'
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'
import AccountCard from '../accounting/AccountCard'
import BalancesTable from './BalancesTable'
import EthereumAddressCard from '../../components/EthereumAddressCard'
import PeerBalances from './PeerBalances'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context as BeeContext } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings'
import { useAccounting } from '../../hooks/accounting'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
width: '100%',
display: 'grid',
rowGap: theme.spacing(3),
},
}),
)
import ExpandableList from '../../components/ExpandableList'
import ExpandableListItem from '../../components/ExpandableListItem'
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
import WithdrawModal from '../../containers/WithdrawModal'
import DepositModal from '../../containers/DepositModal'
export default function Accounting(): ReactElement {
const classes = useStyles()
const { status, nodeAddresses, chequebookAddress, chequebookBalance, settlements, peerBalances } =
useContext(BeeContext)
const { beeDebugApi } = useContext(SettingsContext)
const { accounting, isLoadingUncashed } = useAccounting(beeDebugApi, settlements, peerBalances)
const { accounting, totalUncashed, isLoadingUncashed } = useAccounting(beeDebugApi, settlements, peerBalances)
if (!status.all) return <TroubleshootConnectionCard />
return (
<div className={classes.root}>
<AccountCard
chequebookAddress={chequebookAddress}
chequebookBalance={chequebookBalance}
totalsent={settlements?.totalSent}
totalreceived={settlements?.totalReceived}
<div>
<ExpandableList label="Chequebook" defaultOpen>
<ExpandableListItem label="Total Balance" value={`${chequebookBalance?.totalBalance.toFixedDecimal()} BZZ`} />
<ExpandableListItem
label="Available Uncommitted Balance"
value={`${chequebookBalance?.availableBalance.toFixedDecimal()} BZZ`}
/>
<EthereumAddressCard nodeAddresses={nodeAddresses} chequebookAddress={chequebookAddress} />
<BalancesTable accounting={accounting} isLoadingUncashed={isLoadingUncashed} />
<ExpandableListItem
label="Total Cheques Amount Sent"
value={`${settlements?.totalSent.toFixedDecimal()} BZZ`}
/>
<ExpandableListItem
label="Total Cheques Amount Received"
value={`${settlements?.totalReceived.toFixedDecimal()} BZZ`}
/>
<ExpandableListItemActions>
<WithdrawModal />
<DepositModal />
</ExpandableListItemActions>
</ExpandableList>
<ExpandableList label="Blockchain" defaultOpen>
<ExpandableListItemKey label="Ethereum address" value={nodeAddresses?.ethereum || ''} />
<ExpandableListItemKey label="Chequebook contract address" value={chequebookAddress?.chequebookAddress || ''} />
</ExpandableList>
<PeerBalances accounting={accounting} isLoadingUncashed={isLoadingUncashed} totalUncashed={totalUncashed} />
</div>
)
}
+14 -52
View File
@@ -1,66 +1,28 @@
import { ReactElement, useState, useContext } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Paper, InputBase, IconButton, FormHelperText } from '@material-ui/core'
import { Search } from '@material-ui/icons'
import { Context as SettingsContext } from '../../providers/Settings'
import ExpandableListItemInput from '../../components/ExpandableListItemInput'
import { Utils } from '@ethersphere/bee-js'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
padding: theme.spacing(0.25),
display: 'flex',
alignItems: 'center',
},
input: {
marginLeft: theme.spacing(1),
flex: 1,
},
iconButton: {
padding: 10,
},
divider: {
height: 28,
margin: 4,
},
}),
)
export default function Files(): ReactElement {
const classes = useStyles()
const { apiUrl } = useContext(SettingsContext)
const [referenceInput, setReferenceInput] = useState('')
const [referenceError, setReferenceError] = useState<Error | null>(null)
const [referenceError, setReferenceError] = useState<string | undefined>(undefined)
const handleReferenceChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setReferenceInput(e.target.value)
if (Utils.Hex.isHexString(e.target.value, 64) || Utils.Hex.isHexString(e.target.value, 128)) setReferenceError(null)
else setReferenceError(new Error('Incorrect format of swarm hash'))
const validateChange = (value: string) => {
if (Utils.isHexString(value, 64) || Utils.isHexString(value, 128)) setReferenceError(undefined)
else setReferenceError('Incorrect format of swarm hash. Expected 64 or 128 hexstring characters.')
}
return (
<>
<Paper className={classes.root}>
<InputBase
className={classes.input}
placeholder="Enter swarm reference e.g. 0773a91efd6547c754fc1d95fb1c62c7d1b47f959c2caa685dfec8736da95c1c"
inputProps={{ 'aria-label': 'retrieve file from swarm' }}
value={referenceInput}
onChange={handleReferenceChange}
<ExpandableListItemInput
label="Swarm Hash"
onConfirm={value => window.open(`${apiUrl}/bzz/${value}`, '_blank')}
onChange={validateChange}
helperText={referenceError}
confirmLabel={'Download'}
confirmLabelDisabled={Boolean(referenceError)}
placeholder="e.g. 31fb0362b1a42536134c86bc58b97ac0244e5c6630beec3e27c2d1cecb38c605"
expandedOnly
/>
<IconButton
href={`${apiUrl}/bzz/${referenceInput}`}
target="_blank"
disabled={referenceError !== null || !referenceInput}
className={classes.iconButton}
aria-label="download"
>
<Search />
</IconButton>
</Paper>
{referenceError && <FormHelperText error>{referenceError.message}</FormHelperText>}
</>
)
}
+4 -8
View File
@@ -1,9 +1,5 @@
import Button from '@material-ui/core/Button'
import ListItemIcon from '@material-ui/core/ListItemIcon'
import Menu from '@material-ui/core/Menu'
import MenuItem from '@material-ui/core/MenuItem'
import React, { ReactElement } from 'react'
import PeerDetailDrawer from '../../components/PeerDetail'
import { Button, ListItemIcon, Typography, Menu, MenuItem } from '@material-ui/core'
import { EnrichedPostageBatch } from '../../providers/Stamps'
interface Props {
@@ -25,10 +21,10 @@ export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props
return (
<div>
<Button aria-controls="simple-menu" aria-haspopup="true" onClick={handleClick}>
<Button variant="contained" aria-haspopup="true" onClick={handleClick}>
Change
</Button>
<Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
<Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
{stamps.map(stamp => (
<MenuItem
key={stamp.batchID}
@@ -39,7 +35,7 @@ export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props
selected={stamp.batchID === selectedStamp?.batchID}
>
<ListItemIcon>{stamp.usageText}</ListItemIcon>
<PeerDetailDrawer peerId={stamp.batchID} />
<Typography variant="body2">{stamp.batchID.substr(0, 8)}[]</Typography>
</MenuItem>
))}
</Menu>
+84 -35
View File
@@ -1,20 +1,31 @@
import { Button, CircularProgress, Container } from '@material-ui/core'
import Avatar from '@material-ui/core/Avatar'
import Chip from '@material-ui/core/Chip'
import { Button, CircularProgress, Container, Avatar, Chip, Typography } from '@material-ui/core'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { DropzoneArea } from 'material-ui-dropzone'
import { useSnackbar } from 'notistack'
import { RotateCcw, Check } from 'react-feather'
import { ReactElement, useContext, useEffect, useState } from 'react'
import UploadSizeAlert from '../../components/AlertUploadSize'
import ClipboardCopy from '../../components/ClipboardCopy'
import PeerDetailDrawer from '../../components/PeerDetail'
import { Context, EnrichedPostageBatch } from '../../providers/Stamps'
import { Context as SettingsContext } from '../../providers/Settings'
import CreatePostageStamp from '../stamps/CreatePostageStampModal'
import SelectStamp from './SelectStamp'
import ExpandableListItem from '../../components/ExpandableListItem'
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
import ExpandableListItemNote from '../../components/ExpandableListItemNote'
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
content: { marginTop: theme.spacing(2) },
loadingProgress: { textAlign: 'center', padding: '50px' },
}),
)
const MAX_FILE_SIZE = 1_000_000_000 // 1 gigabyte
export default function Files(): ReactElement {
const classes = useStyles()
const [dropzoneKey, setDropzoneKey] = useState(0)
const [file, setFile] = useState<File | null>(null)
const [uploadReference, setUploadReference] = useState('')
@@ -22,10 +33,14 @@ export default function Files(): ReactElement {
const [selectedStamp, setSelectedStamp] = useState<EnrichedPostageBatch | null>(null)
const { isLoading, error, stamps } = useContext(Context)
const { isLoading, error, stamps, refresh } = useContext(Context)
const { beeApi } = useContext(SettingsContext)
const { enqueueSnackbar } = useSnackbar()
useEffect(() => {
refresh()
}, [])
// Choose a postage stamp that has the lowest usage
useEffect(() => {
if (!selectedStamp && stamps && stamps.length > 0) {
@@ -47,68 +62,102 @@ export default function Files(): ReactElement {
setIsUploadingFile(true)
beeApi
.uploadFile(selectedStamp.batchID, file)
.then(hash => {
window.setTimeout(() => {
setFile(null)
setUploadReference(hash)
setDropzoneKey(dropzoneKey + 1)
}, 0)
})
.then(hash => setUploadReference(hash.reference))
.catch(e => enqueueSnackbar(`Error uploading: ${e.message}`, { variant: 'error' }))
.finally(() => {
setIsUploadingFile(false)
})
.finally(() => setIsUploadingFile(false))
}
const uploadNew = () => {
setTimeout(() => {
setFile(null)
setDropzoneKey(dropzoneKey + 1)
setUploadReference('')
}, 0)
}
const handleChange = (files?: File[]) => {
setUploadReference('')
if (files) {
setFile(files[0])
}
}
return (
<div>
<div>
<>
<DropzoneArea
key={'dropzone-' + dropzoneKey}
onChange={handleChange}
filesLimit={1}
maxFileSize={MAX_FILE_SIZE}
/>
<div style={{ marginTop: '15px' }}>
<div className={classes.content}>
{/* We have file and can upload display stamp selection */}
{file && !isUploadingFile && !uploadReference && (
<>
<ExpandableListItemNote>
To upload this file to your node, you need a postage stamp. You can buy a new one or you can use an
existing stamp (providing its sufficient for this file).
</ExpandableListItemNote>
{selectedStamp && (
<div style={{ display: 'flex' }}>
<small>
with Postage Stamp{' '}
<ExpandableListItem
label={
<>
Upload with Postage Stamp{' '}
<Chip
avatar={<Avatar>{selectedStamp.usageText}</Avatar>}
label={<PeerDetailDrawer peerId={selectedStamp.batchID} characterLength={6} />}
label={<Typography variant="body2">{selectedStamp.batchID.substr(0, 8)}[]</Typography>}
deleteIcon={<ClipboardCopy value={selectedStamp.batchID} />}
onDelete={() => {} /* eslint-disable-line*/}
variant="outlined"
/>
</small>
<SelectStamp stamps={stamps} selectedStamp={selectedStamp} setSelected={setSelectedStamp} />
</div>
</>
}
value={<SelectStamp stamps={stamps} selectedStamp={selectedStamp} setSelected={setSelectedStamp} />}
/>
)}
{!selectedStamp && <CreatePostageStamp />}
<Button disabled={!file && isUploadingFile && !selectedStamp} onClick={() => uploadFile()}>
{!selectedStamp && (
<ExpandableListItemActions>
<CreatePostageStamp />
</ExpandableListItemActions>
)}
</>
)}
{/* We have file and can upload display upload button */}
{file && !uploadReference && (
<>
<ExpandableListItemActions>
<Button
variant="contained"
disabled={!file && isUploadingFile && !selectedStamp}
onClick={() => uploadFile()}
startIcon={<Check size="1rem" />}
>
Upload
</Button>
{file && <UploadSizeAlert file={file} />}
{isUploadingFile && (
<Container style={{ textAlign: 'center', padding: '50px' }}>
<Container className={classes.loadingProgress}>
<CircularProgress />
</Container>
)}
</ExpandableListItemActions>
<UploadSizeAlert file={file} />
</>
)}
{/* File has already been uploaded */}
{uploadReference && (
<div style={{ marginBottom: '15px', display: 'flex' }}>
<span>{uploadReference}</span>
<ClipboardCopy value={uploadReference} />
</div>
<>
<ExpandableListItemKey label="Swarm Reference" value={uploadReference} />
<ExpandableListItemActions>
<Button variant="contained" onClick={uploadNew} startIcon={<RotateCcw size="1rem" />}>
Upload New File
</Button>
</ExpandableListItemActions>
</>
)}
</div>
</div>
</div>
</>
)
}
+3 -7
View File
@@ -1,20 +1,17 @@
import { ReactElement, useContext } from 'react'
import { Container } from '@material-ui/core'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context } from '../../providers/Bee'
import Download from './Download'
import Upload from './Upload'
import TabsContainer from '../../components/TabsContainer'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context as BeeContext } from '../../providers/Bee'
export default function Files(): ReactElement {
const { status } = useContext(Context)
const { status } = useContext(BeeContext)
if (!status.all) return <TroubleshootConnectionCard />
return (
<Container maxWidth="sm">
<TabsContainer
values={[
{
@@ -27,6 +24,5 @@ export default function Files(): ReactElement {
},
]}
/>
</Container>
)
}
-113
View File
@@ -1,113 +0,0 @@
import { ReactElement, useState } from 'react'
import { Link } from 'react-router-dom'
import { createStyles, makeStyles } from '@material-ui/core/styles'
import { Card, CardContent, Typography, Chip, Button } from '@material-ui/core/'
import { ArrowRight, ArrowDropUp } from '@material-ui/icons/'
import { NodeAddresses, Topology } from '@ethersphere/bee-js'
import { ROUTES } from '../../routes'
const useStyles = makeStyles(() =>
createStyles({
root: {
display: 'flex',
flex: '1 1 auto',
flexDirection: 'column',
},
status: {
color: '#2145a0',
backgroundColor: '#e1effe',
},
}),
)
interface Props {
nodeAddresses: NodeAddresses | null
nodeTopology: Topology | null
userBeeVersion?: string
isLatestBeeVersion: boolean
isOk: boolean
latestUrl: string
}
function StatusCard({
userBeeVersion,
nodeAddresses,
nodeTopology,
isLatestBeeVersion,
latestUrl,
}: Props): ReactElement | null {
const classes = useStyles()
const [underlayAddressesVisible, setUnderlayAddresessVisible] = useState<boolean>(false)
return (
<Card>
<CardContent className={classes.root}>
<>
<div style={{ marginBottom: '20px' }}>
<span style={{ marginRight: '20px' }}>Discovered Nodes: {nodeTopology?.population}</span>
<span style={{ marginRight: '20px' }}>
<span>Connected Peers: </span>
<Link to={ROUTES.PEERS}>{nodeTopology?.connected}</Link>
</span>
</div>
<div>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>AGENT: </span>
<a href="https://github.com/ethersphere/bee" rel="noreferrer" target="_blank">
Bee
</a>{' '}
<span>{userBeeVersion || '-'}</span>
{isLatestBeeVersion ? (
<Chip
style={{ marginLeft: '7px', color: '#2145a0' }}
size="small"
label="latest"
className={classes.status}
/>
) : (
<Button size="small" variant="outlined" href={latestUrl}>
update
</Button>
)}
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>PUBLIC KEY: </span>
<span>{nodeAddresses?.publicKey ? nodeAddresses.publicKey : '-'}</span>
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>PSS PUBLIC KEY: </span>
<span>{nodeAddresses?.pssPublicKey ? nodeAddresses.pssPublicKey : '-'}</span>
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<span>OVERLAY ADDRESS (PEER ID): </span>
<span>{nodeAddresses?.overlay ? nodeAddresses.overlay : '-'}</span>
</Typography>
<Typography component="div" variant="subtitle2" gutterBottom>
<Typography component="div" onClick={() => setUnderlayAddresessVisible(!underlayAddressesVisible)}>
<Button color="primary" style={{ padding: 0, marginTop: '6px' }}>
{underlayAddressesVisible ? (
<ArrowDropUp style={{ fontSize: '12px' }} />
) : (
<ArrowRight style={{ fontSize: '12px' }} />
)}
<span>Underlay Addresses</span>
</Button>
</Typography>
{underlayAddressesVisible && (
<div>
{nodeAddresses?.underlay.map(item => (
<li key={item}>{item}</li>
))}
</div>
)}
</Typography>
</div>
</>
</CardContent>
</Card>
)
}
export default StatusCard
+37 -26
View File
@@ -1,24 +1,14 @@
import { ReactElement, useContext } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Button } from '@material-ui/core'
import StatusCard from './StatusCard'
import EthereumAddressCard from '../../components/EthereumAddressCard'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context as BeeContext } from '../../providers/Bee'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
width: '100%',
display: 'grid',
rowGap: theme.spacing(3),
},
}),
)
import ExpandableList from '../../components/ExpandableList'
import ExpandableListItem from '../../components/ExpandableListItem'
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
import TopologyStats from '../../components/TopologyStats'
export default function Status(): ReactElement {
const classes = useStyles()
const {
status,
latestUserVersion,
@@ -32,18 +22,39 @@ export default function Status(): ReactElement {
if (!status.all) return <TroubleshootConnectionCard />
return (
<div className={classes.root}>
<StatusCard
userBeeVersion={latestUserVersion}
isLatestBeeVersion={isLatestBeeVersion}
isOk={status.all}
nodeTopology={topology}
latestUrl={latestBeeVersionUrl}
nodeAddresses={nodeAddresses}
<div>
<ExpandableList label="Bee Node" defaultOpen>
<ExpandableListItem
label="Agent"
value={
<div>
<a href="https://github.com/ethersphere/bee" rel="noreferrer" target="_blank">
Bee
</a>
{` ${latestUserVersion || '-'} `}
<Button size="small" variant="outlined" href={latestBeeVersionUrl} target="_blank">
{isLatestBeeVersion ? 'latest' : 'update'}
</Button>
</div>
}
/>
{nodeAddresses && chequebookAddress && (
<EthereumAddressCard nodeAddresses={nodeAddresses} chequebookAddress={chequebookAddress} />
)}
<ExpandableListItemKey label="Public key" value={nodeAddresses?.publicKey || ''} />
<ExpandableListItemKey label="PSS public key" value={nodeAddresses?.pssPublicKey || ''} />
<ExpandableListItemKey label="Overlay address (Peer ID)" value={nodeAddresses?.overlay || ''} />
<ExpandableList level={1} label="Underlay addresses">
{nodeAddresses?.underlay.map(addr => (
<ExpandableListItem key={addr} value={addr} />
))}
</ExpandableList>
</ExpandableList>
<ExpandableList label="Blockchain" defaultOpen>
<ExpandableListItemKey label="Ethereum address" value={nodeAddresses?.ethereum || ''} />
<ExpandableListItemKey label="Chequebook contract address" value={chequebookAddress?.chequebookAddress || ''} />
</ExpandableList>
<ExpandableList label="Connectivity" defaultOpen>
<TopologyStats topology={topology} />
</ExpandableList>
</div>
)
}
-95
View File
@@ -1,95 +0,0 @@
import { ReactElement, useState, useContext } from 'react'
import { makeStyles } from '@material-ui/core/styles'
import {
Table,
TableBody,
TableCell,
TableContainer,
TableRow,
TableHead,
Button,
Paper,
Tooltip,
CircularProgress,
} from '@material-ui/core'
import { Autorenew } from '@material-ui/icons'
import { Context as SettingsContext } from '../../providers/Settings'
import type { Peer } from '@ethersphere/bee-js'
const useStyles = makeStyles({
table: {
minWidth: 650,
},
})
interface Props {
peers: Peer[] | null
}
interface PeerLatency {
rtt: string
loading: boolean
}
function getPingState(peerLatency: Record<string, PeerLatency>, peer: Peer): ReactElement {
if (peerLatency[peer.address]?.loading) return <CircularProgress size={20} />
if (peerLatency[peer.address]?.rtt) return <span>{peerLatency[peer.address]?.rtt}</span>
return <Autorenew />
}
function PeerTable(props: Props): ReactElement {
const classes = useStyles()
const { beeDebugApi } = useContext(SettingsContext)
const [peerLatency, setPeerLatency] = useState<Record<string, PeerLatency>>({})
const pingPeer = (peerId: string) => {
setPeerLatency(prevPeerLatency => ({ ...prevPeerLatency, [peerId]: { rtt: '', loading: true } }))
beeDebugApi
?.pingPeer(peerId)
.then(res => {
setPeerLatency(prevPeerLatency => ({ ...prevPeerLatency, [peerId]: { rtt: res.rtt, loading: false } }))
})
.catch(() => {
setPeerLatency(prevPeerLatency => ({ ...prevPeerLatency, [peerId]: { rtt: 'error', loading: false } }))
})
}
return (
<div>
<TableContainer component={Paper}>
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>Index</TableCell>
<TableCell>Peer Id</TableCell>
<TableCell align="right">Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{props.peers?.map((peer: Peer, idx: number) => (
<TableRow key={peer.address}>
<TableCell component="th" scope="row">
{idx + 1}
</TableCell>
<TableCell>{peer.address}</TableCell>
<TableCell align="right">
<Tooltip title="Ping node">
<Button color="primary" onClick={() => pingPeer(peer.address)}>
{getPingState(peerLatency, peer)}
</Button>
</Tooltip>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</div>
)
}
export default PeerTable
-21
View File
@@ -1,21 +0,0 @@
import PeerTable from './PeerTable'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context } from '../../providers/Bee'
import TopologyStats from '../../components/TopologyStats'
import { ReactElement, useContext } from 'react'
export default function Peers(): ReactElement {
const { topology, peers, status } = useContext(Context)
if (!status.all) {
return <TroubleshootConnectionCard />
}
return (
<>
<TopologyStats topology={topology} />
<PeerTable peers={peers} />
</>
)
}
+7 -62
View File
@@ -1,70 +1,15 @@
import React, { ReactElement, useState, useContext } from 'react'
import { Paper, Container, TextField, Typography, Button } from '@material-ui/core'
import { ReactElement, useContext } from 'react'
import { Context as SettingsContext } from '../../providers/Settings'
import ExpandableList from '../../components/ExpandableList'
import ExpandableListItemInput from '../../components/ExpandableListItemInput'
export default function Settings(): ReactElement {
const { apiUrl, apiDebugUrl, setApiUrl, setDebugApiUrl } = useContext(SettingsContext)
const [host, setHost] = useState(apiUrl)
const [debugHost, setDebugHost] = useState(apiDebugUrl)
const submit = () => {
if (host !== apiUrl) setApiUrl(host)
if (debugHost !== apiDebugUrl) setDebugApiUrl(debugHost)
}
const touched = host !== apiUrl || debugHost !== apiDebugUrl
return (
<div>
<Container>
<Typography variant="h4" gutterBottom>
Settings
</Typography>
<Paper>
<TextField
label="API Endpoint"
style={{ margin: 0 }}
placeholder="ex: 127.0.0.0.1:1633"
helperText="Enter node host override / port"
fullWidth
defaultValue={apiUrl}
margin="normal"
InputLabelProps={{
shrink: true,
}}
onChange={e => {
setHost(e.target.value)
}}
variant="filled"
/>
</Paper>
<Paper style={{ marginTop: '20px' }}>
<TextField
label="Debug API Endpoint"
style={{ margin: 0 }}
placeholder="ex: 127.0.0.0.1:1635"
helperText="Enter node debug host override / port"
fullWidth
defaultValue={apiDebugUrl}
onChange={e => {
setDebugHost(e.target.value)
}}
margin="normal"
InputLabelProps={{
shrink: true,
}}
variant="filled"
/>
</Paper>
{touched ? (
<div style={{ marginTop: '20px' }}>
<Button variant="outlined" color="primary" onClick={submit}>
Save
</Button>
</div>
) : null}
</Container>
</div>
<ExpandableList label="API Settings" defaultOpen>
<ExpandableListItemInput label="Bee API" value={apiUrl} onConfirm={setApiUrl} />
<ExpandableListItemInput label="Bee Debug API" value={apiDebugUrl} onConfirm={setDebugApiUrl} />
</ExpandableList>
)
}
+2 -3
View File
@@ -111,7 +111,7 @@ export default function FormDialog({ label }: Props): ReactElement {
>
{({ submitForm, isValid, isSubmitting, values }) => (
<Form>
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
<Button variant="contained" onClick={handleClickOpen}>
{label || 'Buy Postage Stamp'}
{isSubmitting && <CircularProgress size={24} className={classes.buttonProgress} />}
</Button>
@@ -138,12 +138,11 @@ export default function FormDialog({ label }: Props): ReactElement {
<Field component={TextField} name="label" label="Label" fullWidth className={classes.field} />
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
<Button onClick={handleClose} variant="contained">
Cancel
</Button>
<div className={classes.wrapper}>
<Button
color="primary"
disabled={isSubmitting || !isValid || !values.amount || !values.depth}
type="submit"
variant="contained"
+9 -37
View File
@@ -1,53 +1,25 @@
import { Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import type { ReactElement } from 'react'
import ClipboardCopy from '../../components/ClipboardCopy'
import PeerDetailDrawer from '../../components/PeerDetail'
import { EnrichedPostageBatch } from '../../providers/Stamps'
import ExpandableList from '../../components/ExpandableList'
import ExpandableListItem from '../../components/ExpandableListItem'
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
const useStyles = makeStyles({
table: {
minWidth: 650,
},
values: {
textAlign: 'right',
fontFamily: 'monospace, monospace',
},
})
interface Props {
postageStamps: EnrichedPostageBatch[] | null
}
function StampsTable({ postageStamps }: Props): ReactElement | null {
if (postageStamps === null) return null
const classes = useStyles()
return (
<TableContainer component={Paper}>
<Table className={classes.table} size="small" aria-label="Balances Table">
<TableHead>
<TableRow>
<TableCell>Batch ID</TableCell>
<TableCell align="right">Usage</TableCell>
</TableRow>
</TableHead>
<TableBody>
<ExpandableList label="Postage Stamps" defaultOpen>
{postageStamps.map(({ batchID, usageText }) => (
<TableRow key={batchID}>
<TableCell>
<div style={{ display: 'flex' }}>
<small>
<PeerDetailDrawer peerId={batchID} />
</small>
<ClipboardCopy value={batchID} />
</div>
</TableCell>
<TableCell className={classes.values}>{usageText}</TableCell>
</TableRow>
<ExpandableList key={batchID} label={`${batchID.substr(0, 8)}[…]`} level={1} info={`${usageText} used`}>
<ExpandableListItemKey label="Batch ID" value={batchID} />
<ExpandableListItem label="Usage" value={usageText} />
</ExpandableList>
))}
</TableBody>
</Table>
</TableContainer>
</ExpandableList>
)
}
+8 -19
View File
@@ -1,26 +1,23 @@
import { ReactElement, useContext, useEffect } from 'react'
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'
import { createStyles, makeStyles } from '@material-ui/core/styles'
import { Container, CircularProgress } from '@material-ui/core'
import StampsTable from './StampsTable'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import CreatePostageStampModal from './CreatePostageStampModal'
import { Context } from '../../providers/Stamps'
import { Context as StampsContext } from '../../providers/Stamps'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context as BeeContext } from '../../providers/Bee'
const useStyles = makeStyles((theme: Theme) =>
const useStyles = makeStyles(() =>
createStyles({
root: {
width: '100%',
display: 'grid',
rowGap: theme.spacing(2),
},
actions: {
display: 'flex',
width: '100%',
columnGap: theme.spacing(1),
rowGap: theme.spacing(1),
flex: '0 1 auto',
flexWrap: 'wrap',
alignItems: 'center',
@@ -30,25 +27,17 @@ const useStyles = makeStyles((theme: Theme) =>
export default function Accounting(): ReactElement {
const classes = useStyles()
const { stamps, isLoading, error, start, stop } = useContext(StampsContext)
const { status } = useContext(BeeContext)
if (!status.all) return <TroubleshootConnectionCard />
const beeContext = useContext(BeeContext)
const { stamps, isLoading, error, start, stop } = useContext(Context)
useEffect(() => {
start()
return () => stop()
}, [])
if (beeContext.isLoading) {
return (
<Container style={{ textAlign: 'center', padding: '50px' }}>
<CircularProgress />
</Container>
)
}
if (!beeContext.status.all) return <TroubleshootConnectionCard />
return (
<div className={classes.root}>
{error && (
@@ -1,35 +1,48 @@
import { Typography } from '@material-ui/core/'
import EthereumAddress from '../../../components/EthereumAddress'
import { useContext } from 'react'
import DepositModal from '../../../containers/DepositModal'
import type { ReactElement } from 'react'
import ExpandableList from '../../../components/ExpandableList'
import ExpandableListItemKey from '../../../components/ExpandableListItemKey'
import ExpandableListItemActions from '../../../components/ExpandableListItemActions'
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
import StatusIcon from '../../../components/StatusIcon'
import { Context } from '../../../providers/Bee'
interface Props extends StatusHookCommon {
chequebookAddress?: string
}
const ChequebookDeployFund = (): ReactElement | null => {
const { status, isLoading, chequebookAddress } = useContext(Context)
const isOk = status.chequebook
const ChequebookDeployFund = ({ chequebookAddress, isOk }: Props): ReactElement | null => {
return (
<div>
<p style={{ marginBottom: '20px', display: 'flex' }}>{chequebookAddress && <DepositModal />}</p>
<div style={{ marginBottom: '10px' }}>
{!isOk && (
<div>
<span>
<ExpandableList
label={
<>
<StatusIcon isOk={isOk} isLoading={isLoading} /> Chequebook Deployment & Funding
</>
}
>
<ExpandableListItemNote>
{isOk ? (
'Your chequebook is deployed and funded'
) : (
<>
Your chequebook is either not deployed or funded. To run the node you will need xDAI and xBZZ on the xDai
network. You may need to aquire BZZ through (e.g. <a href="https://bzz.exchange/">bzz.exchange</a>) and
bridge it to the xDai network through the <a href="https://omni.xdaichain.com/bridge">omni bridge</a>. To
pay the transaction fees, you will also need xDAI token. You can purchase DAI on the network and bridge it
to xDai network through the <a href="https://bridge.xdaichain.com/">xDai Bridge</a>. See the{' '}
network. You may need to aquire BZZ (e.g. <a href="https://bzz.exchange/">bzz.exchange</a>) and bridge it to
the xDai network through the <a href="https://omni.xdaichain.com/bridge">omni bridge</a>. To pay the
transaction fees, you will also need xDAI token. You can purchase DAI on the network and bridge it to xDai
network through the <a href="https://bridge.xdaichain.com/">xDai Bridge</a>. See the{' '}
<a href="https://www.xdaichain.com/#xdai-stable-chain">official xDai website</a> for more information.
</span>
</div>
</>
)}
</div>
<Typography variant="subtitle1" gutterBottom>
Chequebook Address
</Typography>
<EthereumAddress address={chequebookAddress} />
</div>
</ExpandableListItemNote>
{chequebookAddress && (
<>
<ExpandableListItemKey label="Chequebook Address" value={chequebookAddress.chequebookAddress} />
<ExpandableListItemActions>
<DepositModal />
</ExpandableListItemActions>
</>
)}
</ExpandableList>
)
}
@@ -1,57 +1,46 @@
import { ReactElement, useContext } from 'react'
import { Typography, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core/'
import MuiAlert from '@material-ui/lab/Alert'
import { ExpandMoreSharp } from '@material-ui/icons/'
import ConnectToHost from '../../../components/ConnectToHost'
import CodeBlockTabs from '../../../components/CodeBlockTabs'
import ExpandableList from '../../../components/ExpandableList'
import ExpandableListItem from '../../../components/ExpandableListItem'
import ExpandableListItemInput from '../../../components/ExpandableListItemInput'
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
import StatusIcon from '../../../components/StatusIcon'
import { Context as SettingsContext } from '../../../providers/Settings'
import { Context } from '../../../providers/Bee'
type Props = StatusHookCommon
export default function NodeConnectionCheck({ isOk }: Props): ReactElement | null {
export default function NodeConnectionCheck(): ReactElement | null {
const { status, isLoading } = useContext(Context)
const { setDebugApiUrl, apiDebugUrl } = useContext(SettingsContext)
const changeDebugApiUrl = (
<div style={{ display: 'flex', marginTop: '25px', marginBottom: '25px' }}>
<span style={{ marginRight: '15px' }}>
Debug API (<Typography variant="button">{apiDebugUrl}</Typography>)
</span>
<ConnectToHost
setHost={(host: string) => {
console.log(host) // eslint-disable-line
setDebugApiUrl(host)
}}
defaultHost={apiDebugUrl}
/>
</div>
)
if (isOk) {
return changeDebugApiUrl
}
const isOk = status.debugApiConnection
return (
<div>
{changeDebugApiUrl}
<ExpandableList
label={
<>
<StatusIcon isOk={isOk} isLoading={isLoading} /> Connection to Bee Debug API
</>
}
>
<ExpandableListItemNote>
{isOk
? 'The connection to the Bee nodes deug API has been successful'
: 'We cannot connect to your nodes debug API. Please check the following to troubleshoot your issue.'}
</ExpandableListItemNote>
<ExpandableListItemInput label="Bee Debug API" value={apiDebugUrl} onConfirm={setDebugApiUrl} />
<div>
<Typography component="div" variant="body2" gutterBottom style={{ margin: '15px' }}>
We cannot connect to your nodes debug API at <Typography variant="button">{apiDebugUrl}</Typography>. Please
check the following to troubleshoot your issue.
<Accordion style={{ marginTop: '20px' }}>
<AccordionSummary expandIcon={<ExpandMoreSharp />} aria-controls="panel1a-content" id="panel1a-header">
<Typography>Troubleshoot</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography component="div">
{!isOk && (
<ExpandableList level={1} label="Troubleshoot">
<ExpandableListItem
label={
<ol>
<li>Check the status of your node by running the below command to see if your node is running.</li>
<CodeBlockTabs showLineNumbers linux={`sudo systemctl status bee`} mac={`brew services list`} />
<li>
If your node is running, check your firewall settings to make sure that port 1635 (or your custom
specified port) is bound to localhost. If your node is not running try executing the below command
to start your bee node
specified port) is bound to localhost. If your node is not running try executing the below command to
start your bee node
</li>
<MuiAlert
style={{ marginTop: '10px', marginBottom: '10px' }}
@@ -59,10 +48,10 @@ export default function NodeConnectionCheck({ isOk }: Props): ReactElement | nul
variant="filled"
severity="error"
>
Your debug node API should never be completely open to the internet. If you want to connect
remotely, make sure your firewall settings are set to only allow specific trusted IP addresses and
block all other ports. A simple google search for &quot;what is my ip&quot; will show you your
computers public IP address to allow.
Your debug node API should never be completely open to the internet. If you want to connect remotely,
make sure your firewall settings are set to only allow specific trusted IP addresses and block all
other ports. A simple google search for &quot;what is my ip&quot; will show you your computers public
IP address to allow.
</MuiAlert>
<CodeBlockTabs
showLineNumbers
@@ -80,8 +69,8 @@ export default function NodeConnectionCheck({ isOk }: Props): ReactElement | nul
Origin Resource Sharing (CORS) setting is configured to allow your host. Config parameter{' '}
<strong>debug-api-enable</strong> must be set to <strong>true</strong> and{' '}
<strong>cors-allowed-origins</strong> must be set to your host domain or IP (you can also use the
wildcard <code>{"cors-allowed-origins: ['*']"}</code>). If edits are made to the configuration run
the restart command below for changes to take effect.
wildcard <code>{"cors-allowed-origins: ['*']"}</code>). If edits are made to the configuration run the
restart command below for changes to take effect.
</li>
<CodeBlockTabs
showLineNumbers
@@ -89,11 +78,10 @@ export default function NodeConnectionCheck({ isOk }: Props): ReactElement | nul
mac={`sudo vi /usr/local/etc/swarm-bee/bee.yaml \nbrew services restart swarm-bee`}
/>
</ol>
</Typography>
</AccordionDetails>
</Accordion>
</Typography>
</div>
</div>
}
/>
</ExpandableList>
)}
</ExpandableList>
)
}
@@ -1,23 +1,27 @@
import type { ReactElement } from 'react'
import { Typography } from '@material-ui/core/'
import EthereumAddress from '../../../components/EthereumAddress'
import { ReactElement, useContext } from 'react'
import ExpandableList from '../../../components/ExpandableList'
import ExpandableListItemKey from '../../../components/ExpandableListItemKey'
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
import StatusIcon from '../../../components/StatusIcon'
import { Context } from '../../../providers/Bee'
type Props = StatusEthereumConnectionHook
export default function EthereumConnectionCheck(): ReactElement | null {
const { status, isLoading, nodeAddresses } = useContext(Context)
const isOk = status.blockchainConnection
export default function EthereumConnectionCheck({ isOk, nodeAddresses }: Props): ReactElement | null {
if (isOk) {
return (
<div>
<Typography variant="subtitle1" gutterBottom>
Node Address
</Typography>
<EthereumAddress address={nodeAddresses?.ethereum} />
</div>
)
<ExpandableList
label={
<>
<StatusIcon isOk={isOk} isLoading={isLoading} /> Connection to Blockchain
</>
}
return (
<p>
>
<ExpandableListItemNote>
{isOk ? (
'Your node is connected to the xDai blockchain'
) : (
<>
Your Bee node must have access to the xDai blockchain, so that it can interact and deploy your chequebook
contract. You can run{' '}
<a href="https://www.xdaichain.com/" rel="noreferrer" target="_blank">
@@ -27,8 +31,12 @@ export default function EthereumConnectionCheck({ isOk, nodeAddresses }: Props):
<a href="https://getblock.io/" rel="noreferrer" target="_blank">
Getblock
</a>
. By default, Bee expects a local node at http://localhost:8545. To use a provider instead, simply change the{' '}
<strong>swap-endpoint</strong> in your configuration file.
</p>
. By default, Bee expects a local node at http://localhost:8545. To use a provider instead, simply change
the <strong>swap-endpoint</strong> in your configuration file.
</>
)}
</ExpandableListItemNote>
{nodeAddresses?.ethereum && <ExpandableListItemKey label="Ethereum Address" value={nodeAddresses?.ethereum} />}
</ExpandableList>
)
}
@@ -1,35 +1,37 @@
import { ReactElement, useContext } from 'react'
import { Typography, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core/'
import { ExpandMoreSharp } from '@material-ui/icons/'
import ConnectToHost from '../../../components/ConnectToHost'
import CodeBlockTabs from '../../../components/CodeBlockTabs'
import { Context as SettingsContext } from '../../../providers/Settings'
import ExpandableList from '../../../components/ExpandableList'
import ExpandableListItem from '../../../components/ExpandableListItem'
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
import ExpandableListItemInput from '../../../components/ExpandableListItemInput'
import StatusIcon from '../../../components/StatusIcon'
import { Context } from '../../../providers/Bee'
type Props = StatusHookCommon
export default function NodeConnectionCheck({ isOk }: Props): ReactElement | null {
export default function NodeConnectionCheck(): ReactElement | null {
const { setApiUrl, apiUrl } = useContext(SettingsContext)
const { status, isLoading } = useContext(Context)
const isOk = status.apiConnection
return (
<div>
<div style={{ display: 'flex', marginBottom: '25px' }}>
<span style={{ marginRight: '15px' }}>
Node API (<Typography variant="button">{apiUrl}</Typography>)
</span>
<ConnectToHost setHost={setApiUrl} defaultHost={apiUrl} />
</div>
<div>
<ExpandableList
label={
<>
<StatusIcon isOk={isOk} isLoading={isLoading} /> Connection to Bee API
</>
}
>
<ExpandableListItemNote>
{isOk
? 'The connection to the Bee nodes API has been successful'
: 'Could not connect to your Bee nodes API. Please check the troubleshoot below on how you may resolve it.'}
</ExpandableListItemNote>
<ExpandableListItemInput label="Bee API" value={apiUrl} onConfirm={setApiUrl} />
{!isOk && (
<Typography component="div" variant="body2" gutterBottom style={{ margin: '15px' }}>
We cannot connect to your nodes API at <Typography variant="button">{apiUrl}</Typography>. Please check the
following to troubleshoot your issue.
<Accordion style={{ marginTop: '20px' }}>
<AccordionSummary expandIcon={<ExpandMoreSharp />} aria-controls="panel1a-content" id="panel1a-header">
<Typography>Troubleshoot</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography component="div">
<ExpandableList level={1} label="Troubleshoot">
<ExpandableListItem
label={
<ol>
<li>Check the status of your node by running the below command to see if your node is running.</li>
<CodeBlockTabs showLineNumbers linux={`sudo systemctl status bee`} mac={`brew services list`} />
@@ -50,12 +52,10 @@ export default function NodeConnectionCheck({ isOk }: Props): ReactElement | nul
mac={`brew services list \ntail -f /usr/local/var/log/swarm-bee/bee.log`}
/>
</ol>
</Typography>
</AccordionDetails>
</Accordion>
</Typography>
}
/>
</ExpandableList>
)}
</div>
</div>
</ExpandableList>
)
}
+20 -34
View File
@@ -1,43 +1,29 @@
import type { ReactElement } from 'react'
import { Typography } from '@material-ui/core/'
import { ReactElement, useContext } from 'react'
import ExpandableList from '../../../components/ExpandableList'
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
import TopologyStats from '../../../components/TopologyStats'
import StatusIcon from '../../../components/StatusIcon'
import { Context } from '../../../providers/Bee'
type Props = StatusTopologyHook
export default function PeerConnection(): ReactElement | null {
const { status, isLoading, topology } = useContext(Context)
const isOk = status.topology
export default function PeerConnection({ isOk, topology }: Props): ReactElement | null {
const peers = (
<div style={{ display: 'flex', marginTop: '15px' }}>
<div style={{ marginRight: '30px' }}>
<Typography component="div" variant="subtitle1" gutterBottom color="textSecondary">
<span>Connected Peers</span>
</Typography>
<Typography component="h2" variant="h5">
{topology?.connected ? topology.connected : '-'}
</Typography>
</div>
<div>
<Typography component="div" variant="subtitle1" gutterBottom color="textSecondary">
<span>Discovered Nodes</span>
</Typography>
<Typography component="h2" variant="h5">
{topology?.population ? topology.population : '-'}
</Typography>
</div>
</div>
)
if (isOk) {
return (
<ExpandableList
label={
<>
<span>You are connected to peers!</span>
{peers}
<StatusIcon isOk={isOk} isLoading={isLoading} /> Connection to Peers
</>
)
}
>
<ExpandableListItemNote>
{isOk
? 'You are connected to other Bee nodes'
: 'Your node is not connected to any peers. Please wait a bit if you just started the node, otherwise review your configuration file.'}
</ExpandableListItemNote>
return (
<>
<span>Your node is not connected to any peers</span>
{peers}
</>
<TopologyStats topology={topology} />
</ExpandableList>
)
}
+26 -39
View File
@@ -1,60 +1,47 @@
import type { ReactElement } from 'react'
import { Typography } from '@material-ui/core/'
import { ReactElement, useContext } from 'react'
import CodeBlockTabs from '../../../components/CodeBlockTabs'
import ExpandableList from '../../../components/ExpandableList'
import ExpandableListItem from '../../../components/ExpandableListItem'
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
import StatusIcon from '../../../components/StatusIcon'
import { Context } from '../../../providers/Bee'
type Props = StatusNodeVersionHook
export default function VersionCheck(): ReactElement | null {
const { status, isLoading, latestUserVersion, latestPublishedVersion, latestBeeVersionUrl } = useContext(Context)
const isOk = status.version
export default function VersionCheck({ isOk, userVersion, latestVersion, latestUrl }: Props): ReactElement | null {
const version = (
<div style={{ display: 'flex' }}>
<div style={{ marginRight: '30px' }}>
<p>
<span>User Version</span>
</p>
<Typography component="h5" variant="h5">
<span>{userVersion}</span>
</Typography>
</div>
<div>
<p>
<span>Latest Version</span>
</p>
<Typography component="h5" variant="h5">
<span>{latestVersion}</span>
</Typography>
</div>
</div>
)
// Running latest bee version
if (isOk) {
return (
<ExpandableList
label={
<>
<span>You are running the latest version of Bee</span>
{version}
<StatusIcon isOk={isOk} isLoading={isLoading} /> Bee Version
</>
)
}
// Old version or not connected to bee debug API
return (
>
<ExpandableListItemNote>
{isOk ? (
'You are running the latest version of Bee.'
) : (
<>
<span>
Your Bee version is out of date. Please update to the{' '}
<a href={latestUrl} rel="noreferrer" target="_blank">
<a href={latestBeeVersionUrl} rel="noreferrer" target="_blank">
latest
</a>{' '}
before continuing. Rerun the installation script below to upgrade. Reference the docs for help with updating.{' '}
before continuing. Rerun the installation script below to upgrade. For more information please see the{' '}
<a href="https://docs.ethswarm.org/docs/installation/manual#upgrading-bee" rel="noreferrer" target="_blank">
Docs
</a>
</span>
.
<CodeBlockTabs
showLineNumbers
linux={`bee version\nwget https://github.com/ethersphere/bee/releases/download/${latestVersion}/bee_${latestVersion}_amd64.deb\nsudo dpkg -i bee_${latestVersion}_amd64.deb`}
linux={`bee version\nwget https://github.com/ethersphere/bee/releases/download/${latestPublishedVersion}/bee_${latestPublishedVersion}_amd64.deb\nsudo dpkg -i bee_${latestPublishedVersion}_amd64.deb`}
mac={`bee version\nbrew tap ethersphere/tap\nbrew install swarm-bee\nbrew services start swarm-bee`}
/>
{version}
</>
)}
</ExpandableListItemNote>
<ExpandableListItem label="Your Version" value={latestUserVersion || '-'} />
<ExpandableListItem label="Latest Version" value={latestPublishedVersion || '-'} />
</ExpandableList>
)
}
+7 -138
View File
@@ -1,7 +1,4 @@
import { ReactElement, useState, useContext } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Typography, Paper, Button, Step, StepLabel, StepContent, Stepper, StepButton } from '@material-ui/core/'
import { CheckCircle, Error, ExpandLessSharp, ExpandMoreSharp, Autorenew } from '@material-ui/icons/'
import type { ReactElement } from 'react'
import DebugConnectionCheck from './SetupSteps/DebugConnectionCheck'
import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck'
@@ -9,144 +6,16 @@ import VersionCheck from './SetupSteps/VersionCheck'
import EthereumConnectionCheck from './SetupSteps/EthereumConnectionCheck'
import ChequebookDeployFund from './SetupSteps/ChequebookDeployFund'
import PeerConnection from './SetupSteps/PeerConnection'
import { Context } from '../../providers/Bee'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
padding: theme.spacing(2),
width: '100%',
},
button: {
marginTop: theme.spacing(1),
marginRight: theme.spacing(1),
},
actionsContainer: {
margin: theme.spacing(2),
},
}),
)
interface Step {
label: string
isOk: boolean
component: ReactElement
}
export default function NodeSetupWorkflow(): ReactElement {
const classes = useStyles()
const [activeStep, setActiveStep] = useState(-1)
const {
status,
isLoading,
latestUserVersion,
latestPublishedVersion,
isLatestBeeVersion,
latestBeeVersionUrl,
topology,
nodeAddresses,
chequebookAddress,
} = useContext(Context)
const steps: Step[] = [
{
label: 'Connected to Node DebugAPI',
isOk: status.debugApiConnection,
component: <DebugConnectionCheck isOk={status.debugApiConnection} />,
},
{
label: 'Running latest Bee version',
isOk: status.version,
component: (
<VersionCheck
isOk={status.version}
isLatestBeeVersion={isLatestBeeVersion}
userVersion={latestUserVersion}
latestVersion={latestPublishedVersion}
latestUrl={latestBeeVersionUrl}
/>
),
},
{
label: 'Connected to xDai Blockchain',
isOk: status.blockchainConnection,
component: <EthereumConnectionCheck isOk={status.blockchainConnection} nodeAddresses={nodeAddresses} />,
},
{
label: 'Deployed and Funded Chequebook',
isOk: status.chequebook,
component: (
<ChequebookDeployFund chequebookAddress={chequebookAddress?.chequebookAddress} isOk={status.chequebook} />
),
},
{
label: 'Connected to Node API',
isOk: status.apiConnection,
component: <NodeConnectionCheck isOk={status.apiConnection} />,
},
{
label: 'Connected to Peers',
isOk: status.topology,
component: <PeerConnection isOk={status.topology} topology={topology} />,
},
]
const handleNext = () => {
setActiveStep(prevActiveStep => prevActiveStep + 1)
}
const handleBack = () => {
setActiveStep(prevActiveStep => prevActiveStep - 1)
}
return (
<Paper className={classes.root}>
<Typography variant="h5" gutterBottom>
Node Setup
</Typography>
<Stepper nonLinear activeStep={activeStep} orientation="vertical">
{steps.map(({ label, isOk, component }, index) => (
<Step key={label}>
<StepLabel
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
StepIconComponent={() => {
if (isLoading) return <Autorenew style={{ height: '25px', cursor: 'pointer' }} />
if (isOk) return <CheckCircle style={{ color: '#32c48d', height: '25px', cursor: 'pointer' }} />
return <Error style={{ color: '#c9201f', height: '25px', cursor: 'pointer' }} />
}}
>
<StepButton
disabled={isLoading}
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
style={{ justifyContent: 'space-between' }}
>
<div style={{ display: 'flex' }}>
<div style={{ marginTop: '5px' }}>{label}</div>
<div style={{ marginLeft: '12px' }}>
{index === activeStep ? <ExpandLessSharp /> : <ExpandMoreSharp />}
</div>
</div>
</StepButton>
</StepLabel>
<StepContent>
<Typography component="div">{component}</Typography>
<div className={classes.actionsContainer}>
<div>
<Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
Back
</Button>
<Button variant="contained" color="primary" onClick={handleNext} className={classes.button}>
{index < steps.length - 1 ? 'Next' : 'Finish'}
</Button>
<DebugConnectionCheck />
<VersionCheck />
<EthereumConnectionCheck />
<ChequebookDeployFund />
<NodeConnectionCheck />
<PeerConnection />
</div>
</div>
</StepContent>
</Step>
))}
</Stepper>
</Paper>
)
}
+1 -4
View File
@@ -6,7 +6,6 @@ import { Route } from 'react-router-dom'
import Info from './pages/info'
import Status from './pages/status'
import Files from './pages/files'
import Peers from './pages/peers'
import Accounting from './pages/accounting'
import Settings from './pages/settings'
import Stamps from './pages/stamps'
@@ -14,7 +13,6 @@ import Stamps from './pages/stamps'
export enum ROUTES {
INFO = '/',
FILES = '/files',
PEERS = '/peers',
ACCOUNTING = '/accounting',
SETTINGS = '/settings',
STAMPS = '/stamps',
@@ -23,13 +21,12 @@ export enum ROUTES {
const BaseRouter = (): ReactElement => (
<Switch>
<Route exact path={ROUTES.INFO} component={Info} />
<Route exact path={ROUTES.FILES} component={Files} />
<Route exact path={ROUTES.PEERS} component={Peers} />
<Route exact path={ROUTES.ACCOUNTING} component={Accounting} />
<Route exact path={ROUTES.SETTINGS} component={Settings} />
<Route exact path={ROUTES.STAMPS} component={Stamps} />
<Route exact path={ROUTES.STATUS} component={Status} />
<Route path={ROUTES.INFO} component={Info} />
</Switch>
)
+152 -26
View File
@@ -9,40 +9,146 @@ declare module '@material-ui/core/styles/createPalette' {
// Overwriting default components styles
const componentsOverrides = (theme: Theme) => ({
MuiDrawer: {
paper: {
width: 300,
backgroundColor: '#212121',
MuiListItem: {
button: {
'&:hover': {
backgroundColor: '#fcf2e8',
color: theme.palette.primary.main,
// https://github.com/mui-org/material-ui/issues/22543
'@media (hover: none)': {
backgroundColor: '#fcf2e8',
color: theme.palette.primary.main,
},
},
},
},
MuiContainer: {
root: { padding: theme.spacing(8) },
maxWidthXs: { padding: theme.spacing(8) },
maxWidthSm: { padding: theme.spacing(8) },
maxWidthMd: { padding: theme.spacing(8) },
maxWidthLg: { padding: theme.spacing(8) },
maxWidthXl: { padding: theme.spacing(8) },
},
MuiButton: {
startIcon: { marginLeft: theme.spacing(1) },
endIcon: { marginRight: theme.spacing(1) },
outlined: {
border: 'none',
borderRadius: theme.spacing(10),
color: theme.palette.primary.main,
backgroundColor: '#fcf2e8',
},
outlinedSizeSmall: {
padding: theme.spacing(1),
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2),
boxShadow: 'none',
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'white',
boxShadow: 'none',
// https://github.com/mui-org/material-ui/issues/22543
'@media (hover: none)': {
backgroundColor: theme.palette.primary.main,
color: 'white',
boxShadow: 'none',
},
},
},
outlinedSizeLarge: {
padding: theme.spacing(4),
borderRadius: 0,
boxShadow: 'none',
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'white',
boxShadow: 'none',
// https://github.com/mui-org/material-ui/issues/22543
'@media (hover: none)': {
backgroundColor: theme.palette.primary.main,
color: 'white',
boxShadow: 'none',
},
},
},
containedSizeLarge: {
padding: theme.spacing(4),
borderRadius: 0,
boxShadow: 'none',
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'white',
boxShadow: 'none',
// https://github.com/mui-org/material-ui/issues/22543
'@media (hover: none)': {
backgroundColor: theme.palette.primary.main,
color: 'white',
boxShadow: 'none',
},
},
},
containedSizeSmall: {
padding: theme.spacing(1),
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2),
borderRadius: 0,
boxShadow: 'none',
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'white',
boxShadow: 'none',
// https://github.com/mui-org/material-ui/issues/22543
'@media (hover: none)': {
backgroundColor: theme.palette.primary.main,
color: 'white',
boxShadow: 'none',
},
},
},
contained: {
padding: theme.spacing(2),
backgroundColor: 'white',
boxShadow: 'none',
borderRadius: 0,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'white',
boxShadow: 'none',
// https://github.com/mui-org/material-ui/issues/22543
'@media (hover: none)': {
backgroundColor: theme.palette.primary.main,
color: 'white',
boxShadow: 'none',
},
},
'&:focus': {
backgroundColor: theme.palette.primary.main,
color: 'white',
},
'&:active': {
backgroundColor: theme.palette.primary.main,
color: 'white',
},
'&:disabled': {
backgroundColor: 'white',
},
},
},
MuiTab: {
root: {
backgroundColor: 'transparent',
fontWeight: theme.typography.fontWeightRegular,
marginRight: theme.spacing(4),
fontFamily: [
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'"Helvetica Neue"',
'Arial',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
].join(','),
backgroundColor: theme.palette.background.paper,
'&:hover': {
color: theme.palette.secondary,
backgroundColor: '#fcf2e8',
color: theme.palette.primary.main,
opacity: 1,
},
'&$selected': {
color: theme.palette.secondary,
fontWeight: theme.typography.fontWeightMedium,
},
'&:focus': {
color: theme.palette.secondary,
},
textColorInherit: {
opacity: 0.5,
},
},
MuiTabs: {
@@ -50,7 +156,7 @@ const componentsOverrides = (theme: Theme) => ({
borderBottom: 'none',
},
indicator: {
backgroundColor: theme.palette.primary.main,
backgroundColor: 'transparent',
},
},
})
@@ -59,16 +165,19 @@ const propsOverrides = {
MuiTab: {
disableRipple: true,
},
MuiButtonBase: {
disableRipple: true,
},
}
export const theme = createMuiTheme({
palette: {
type: 'light',
background: {
default: '#fafafa',
default: '#efefef',
},
primary: {
light: orange.A200,
light: '#fcf2e8',
main: '#dd7700',
dark: orange[800],
},
@@ -78,6 +187,23 @@ export const theme = createMuiTheme({
},
typography: {
fontFamily: ['Work Sans', 'Montserrat', 'Nunito', 'Roboto', '"Helvetica Neue"', 'Arial', 'sans-serif'].join(','),
h1: {
fontSize: '1.3rem',
fontWeight: 500,
},
h2: {
fontSize: '1rem',
fontWeight: 500,
},
h3: {
fontSize: '0.8rem',
fontWeight: 500,
},
body2: {
fontFamily: '"IBM Plex Mono", monospace',
fontWeight: 500,
fontSize: '1rem',
},
},
})
Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB