Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d9e7560117 | |||
| 3a30ee59d4 | |||
| 7880c802ae | |||
| f4013142af | |||
| 57bff96c99 | |||
| a406e0fc01 | |||
| 1310deb17a | |||
| d8787476ac | |||
| bc82e67561 | |||
| 63e79ae2aa | |||
| 48ce9ba659 | |||
| 9ee1c9107b | |||
| a90b4c439b | |||
| 2187b9001c |
@@ -69,3 +69,17 @@ jobs:
|
|||||||
|
|
||||||
- name: Build Component
|
- name: Build Component
|
||||||
run: npm run build:component
|
run: npm run build:component
|
||||||
|
|
||||||
|
- name: Create preview
|
||||||
|
uses: ethersphere/beeload-action@v1
|
||||||
|
with:
|
||||||
|
bee-url: https://unlimited.gateway.ethswarm.org
|
||||||
|
preview: 'true'
|
||||||
|
token: ${{ secrets.REPO_GHA_PAT }}
|
||||||
|
extra-params: '-H "${{ secrets.GATEWAY_AUTHORIZATION_HEADER }}"'
|
||||||
|
|
||||||
|
- name: Upload to testnet
|
||||||
|
continue-on-error: true
|
||||||
|
uses: ethersphere/beeload-action@v1
|
||||||
|
with:
|
||||||
|
bee-url: https://api.gateway.testnet.ethswarm.org
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.13.0](https://www.github.com/ethersphere/bee-dashboard/compare/v0.12.0...v0.13.0) (2022-01-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add hash based routing ([#287](https://www.github.com/ethersphere/bee-dashboard/issues/287)) ([9ee1c91](https://www.github.com/ethersphere/bee-dashboard/commit/9ee1c9107bb08d1838044f39e4d0dd5817c8f283))
|
||||||
|
* add metadata and preview ([#292](https://www.github.com/ethersphere/bee-dashboard/issues/292)) ([f401314](https://www.github.com/ethersphere/bee-dashboard/commit/f4013142afdb407e699eff9587921e23c971f1db))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* clean up spinner and disabled state on download page ([#294](https://www.github.com/ethersphere/bee-dashboard/issues/294)) ([a406e0f](https://www.github.com/ethersphere/bee-dashboard/commit/a406e0fc014991fcbaca230f27f41cd071d8a863))
|
||||||
|
* correct folder name when uploading multiple files or mix of files & directories ([#291](https://www.github.com/ethersphere/bee-dashboard/issues/291)) ([d878747](https://www.github.com/ethersphere/bee-dashboard/commit/d8787476acf068be6609a77b1fadb2f61d0fd502))
|
||||||
|
* disable feeds page when disconnected ([#293](https://www.github.com/ethersphere/bee-dashboard/issues/293)) ([1310deb](https://www.github.com/ethersphere/bee-dashboard/commit/1310deb17aec91f368f99974aaa245abb0a3e201))
|
||||||
|
* do not print size and name when meta is unknown ([#297](https://www.github.com/ethersphere/bee-dashboard/issues/297)) ([7880c80](https://www.github.com/ethersphere/bee-dashboard/commit/7880c802aea6b0830ca52b47b88540b8df5888cc))
|
||||||
|
* get current price from chain state ([#286](https://www.github.com/ethersphere/bee-dashboard/issues/286)) ([bc82e67](https://www.github.com/ethersphere/bee-dashboard/commit/bc82e6756154b33d01796a6e66e51dcfa1495338))
|
||||||
|
|
||||||
## [0.12.0](https://www.github.com/ethersphere/bee-dashboard/compare/v0.11.2...v0.12.0) (2021-12-21)
|
## [0.12.0](https://www.github.com/ethersphere/bee-dashboard/compare/v0.11.2...v0.12.0) (2021-12-21)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Generated
+33
-160
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@ethersphere/bee-dashboard",
|
"name": "@ethersphere/bee-dashboard",
|
||||||
"version": "0.12.0",
|
"version": "0.13.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@ethersphere/bee-dashboard",
|
"name": "@ethersphere/bee-dashboard",
|
||||||
"version": "0.12.0",
|
"version": "0.13.0",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersphere/bee-js": "3.1.0",
|
"@ethersphere/bee-js": "3.1.0",
|
||||||
@@ -31,8 +31,8 @@
|
|||||||
"react-dom": ">= 17.0.2",
|
"react-dom": ">= 17.0.2",
|
||||||
"react-feather": "2.0.9",
|
"react-feather": "2.0.9",
|
||||||
"react-identicons": "1.2.5",
|
"react-identicons": "1.2.5",
|
||||||
"react-router": "5.2.0",
|
"react-router": "6.2.1",
|
||||||
"react-router-dom": "5.2.0",
|
"react-router-dom": "6.2.1",
|
||||||
"react-syntax-highlighter": "15.4.4",
|
"react-syntax-highlighter": "15.4.4",
|
||||||
"semver": "7.3.5",
|
"semver": "7.3.5",
|
||||||
"serve-handler": "6.1.3"
|
"serve-handler": "6.1.3"
|
||||||
@@ -11867,16 +11867,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/history": {
|
"node_modules/history": {
|
||||||
"version": "4.10.1",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz",
|
||||||
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
|
"integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.1.2",
|
"@babel/runtime": "^7.7.6"
|
||||||
"loose-envify": "^1.2.0",
|
|
||||||
"resolve-pathname": "^3.0.0",
|
|
||||||
"tiny-invariant": "^1.0.2",
|
|
||||||
"tiny-warning": "^1.0.0",
|
|
||||||
"value-equal": "^1.0.1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hmac-drbg": {
|
"node_modules/hmac-drbg": {
|
||||||
@@ -13220,11 +13215,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/isarray": {
|
|
||||||
"version": "0.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
|
||||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
|
||||||
},
|
|
||||||
"node_modules/isexe": {
|
"node_modules/isexe": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
@@ -16287,19 +16277,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mini-create-react-context": {
|
|
||||||
"version": "0.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
|
|
||||||
"integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.12.1",
|
|
||||||
"tiny-warning": "^1.0.3"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"prop-types": "^15.0.0",
|
|
||||||
"react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mini-css-extract-plugin": {
|
"node_modules/mini-css-extract-plugin": {
|
||||||
"version": "0.11.3",
|
"version": "0.11.3",
|
||||||
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz",
|
||||||
@@ -17523,14 +17500,6 @@
|
|||||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/path-to-regexp": {
|
|
||||||
"version": "1.8.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
|
|
||||||
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
|
|
||||||
"dependencies": {
|
|
||||||
"isarray": "0.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-type": {
|
"node_modules/path-type": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||||
@@ -21899,47 +21868,29 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "5.2.0",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz",
|
||||||
"integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
|
"integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.1.2",
|
"history": "^5.2.0"
|
||||||
"history": "^4.9.0",
|
|
||||||
"hoist-non-react-statics": "^3.1.0",
|
|
||||||
"loose-envify": "^1.3.1",
|
|
||||||
"mini-create-react-context": "^0.4.0",
|
|
||||||
"path-to-regexp": "^1.7.0",
|
|
||||||
"prop-types": "^15.6.2",
|
|
||||||
"react-is": "^16.6.0",
|
|
||||||
"tiny-invariant": "^1.0.2",
|
|
||||||
"tiny-warning": "^1.0.0"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=15"
|
"react": ">=16.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router-dom": {
|
"node_modules/react-router-dom": {
|
||||||
"version": "5.2.0",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz",
|
||||||
"integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==",
|
"integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.1.2",
|
"history": "^5.2.0",
|
||||||
"history": "^4.9.0",
|
"react-router": "6.2.1"
|
||||||
"loose-envify": "^1.3.1",
|
|
||||||
"prop-types": "^15.6.2",
|
|
||||||
"react-router": "5.2.0",
|
|
||||||
"tiny-invariant": "^1.0.2",
|
|
||||||
"tiny-warning": "^1.0.0"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=15"
|
"react": ">=16.8",
|
||||||
|
"react-dom": ">=16.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router/node_modules/react-is": {
|
|
||||||
"version": "16.13.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
|
||||||
},
|
|
||||||
"node_modules/react-scripts": {
|
"node_modules/react-scripts": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-4.0.3.tgz",
|
||||||
@@ -22636,11 +22587,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/resolve-pathname": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
|
|
||||||
},
|
|
||||||
"node_modules/resolve-url": {
|
"node_modules/resolve-url": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
||||||
@@ -25523,11 +25469,6 @@
|
|||||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/tiny-invariant": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg=="
|
|
||||||
},
|
|
||||||
"node_modules/tiny-warning": {
|
"node_modules/tiny-warning": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
||||||
@@ -26326,11 +26267,6 @@
|
|||||||
"spdx-expression-parse": "^3.0.0"
|
"spdx-expression-parse": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/value-equal": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
|
|
||||||
},
|
|
||||||
"node_modules/vary": {
|
"node_modules/vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
@@ -37639,16 +37575,11 @@
|
|||||||
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
|
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
|
||||||
},
|
},
|
||||||
"history": {
|
"history": {
|
||||||
"version": "4.10.1",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz",
|
||||||
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
|
"integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.1.2",
|
"@babel/runtime": "^7.7.6"
|
||||||
"loose-envify": "^1.2.0",
|
|
||||||
"resolve-pathname": "^3.0.0",
|
|
||||||
"tiny-invariant": "^1.0.2",
|
|
||||||
"tiny-warning": "^1.0.0",
|
|
||||||
"value-equal": "^1.0.1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hmac-drbg": {
|
"hmac-drbg": {
|
||||||
@@ -38664,11 +38595,6 @@
|
|||||||
"is-docker": "^2.0.0"
|
"is-docker": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"isarray": {
|
|
||||||
"version": "0.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
|
||||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
|
||||||
},
|
|
||||||
"isexe": {
|
"isexe": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
@@ -41131,15 +41057,6 @@
|
|||||||
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
|
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"mini-create-react-context": {
|
|
||||||
"version": "0.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
|
|
||||||
"integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.12.1",
|
|
||||||
"tiny-warning": "^1.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mini-css-extract-plugin": {
|
"mini-css-extract-plugin": {
|
||||||
"version": "0.11.3",
|
"version": "0.11.3",
|
||||||
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz",
|
||||||
@@ -42109,14 +42026,6 @@
|
|||||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"path-to-regexp": {
|
|
||||||
"version": "1.8.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
|
|
||||||
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
|
|
||||||
"requires": {
|
|
||||||
"isarray": "0.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"path-type": {
|
"path-type": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||||
@@ -45497,41 +45406,20 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"react-router": {
|
"react-router": {
|
||||||
"version": "5.2.0",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz",
|
||||||
"integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
|
"integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.1.2",
|
"history": "^5.2.0"
|
||||||
"history": "^4.9.0",
|
|
||||||
"hoist-non-react-statics": "^3.1.0",
|
|
||||||
"loose-envify": "^1.3.1",
|
|
||||||
"mini-create-react-context": "^0.4.0",
|
|
||||||
"path-to-regexp": "^1.7.0",
|
|
||||||
"prop-types": "^15.6.2",
|
|
||||||
"react-is": "^16.6.0",
|
|
||||||
"tiny-invariant": "^1.0.2",
|
|
||||||
"tiny-warning": "^1.0.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"react-is": {
|
|
||||||
"version": "16.13.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-router-dom": {
|
"react-router-dom": {
|
||||||
"version": "5.2.0",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz",
|
||||||
"integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==",
|
"integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.1.2",
|
"history": "^5.2.0",
|
||||||
"history": "^4.9.0",
|
"react-router": "6.2.1"
|
||||||
"loose-envify": "^1.3.1",
|
|
||||||
"prop-types": "^15.6.2",
|
|
||||||
"react-router": "5.2.0",
|
|
||||||
"tiny-invariant": "^1.0.2",
|
|
||||||
"tiny-warning": "^1.0.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-scripts": {
|
"react-scripts": {
|
||||||
@@ -46074,11 +45962,6 @@
|
|||||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"resolve-pathname": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
|
|
||||||
},
|
|
||||||
"resolve-url": {
|
"resolve-url": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
||||||
@@ -48406,11 +48289,6 @@
|
|||||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"tiny-invariant": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg=="
|
|
||||||
},
|
|
||||||
"tiny-warning": {
|
"tiny-warning": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
||||||
@@ -49034,11 +48912,6 @@
|
|||||||
"spdx-expression-parse": "^3.0.0"
|
"spdx-expression-parse": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"value-equal": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
|
|
||||||
},
|
|
||||||
"vary": {
|
"vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ethersphere/bee-dashboard",
|
"name": "@ethersphere/bee-dashboard",
|
||||||
"version": "0.12.0",
|
"version": "0.13.0",
|
||||||
"description": "An app which helps users to setup their Bee node and do actions like cash out cheques",
|
"description": "An app which helps users to setup their Bee node and do actions like cash out cheques",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"bee",
|
"bee",
|
||||||
@@ -48,8 +48,8 @@
|
|||||||
"react-dom": ">= 17.0.2",
|
"react-dom": ">= 17.0.2",
|
||||||
"react-feather": "2.0.9",
|
"react-feather": "2.0.9",
|
||||||
"react-identicons": "1.2.5",
|
"react-identicons": "1.2.5",
|
||||||
"react-router": "5.2.0",
|
"react-router": "6.2.1",
|
||||||
"react-router-dom": "5.2.0",
|
"react-router-dom": "6.2.1",
|
||||||
"react-syntax-highlighter": "15.4.4",
|
"react-syntax-highlighter": "15.4.4",
|
||||||
"semver": "7.3.5",
|
"semver": "7.3.5",
|
||||||
"serve-handler": "6.1.3"
|
"serve-handler": "6.1.3"
|
||||||
|
|||||||
+1
-7
@@ -6,13 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
<meta name="description" content="Bee Dashboard" />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600&display=swap" rel="stylesheet">
|
|
||||||
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Bee Dashboard"
|
|
||||||
/>
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
|
|||||||
+37
-8
@@ -1,34 +1,63 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "IBMPlexMono500";
|
font-family: 'Work Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(assets/fonts/WorkSans/WorkSans-Light.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Work Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(assets/fonts/WorkSans/WorkSans-Regular.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Work Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(assets/fonts/WorkSans/WorkSans-Medium.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Work Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(assets/fonts/WorkSans/WorkSans-SemiBold.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'IBMPlexMono500';
|
||||||
src: url(assets/fonts/IBMPlexMono500.ttf) format('truetype');
|
src: url(assets/fonts/IBMPlexMono500.ttf) format('truetype');
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "IBMPlexMono600";
|
font-family: 'IBMPlexMono600';
|
||||||
src: url(assets/fonts/IBMPlexMono600.ttf) format('truetype');
|
src: url(assets/fonts/IBMPlexMono600.ttf) format('truetype');
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "IBMPlexMonoregular";
|
font-family: 'IBMPlexMonoregular';
|
||||||
src: url(assets/fonts/IBMPlexMonoregular.ttf) format('truetype');
|
src: url(assets/fonts/IBMPlexMonoregular.ttf) format('truetype');
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "WorkSans-Italic-VariableFont_wght";
|
font-family: 'WorkSans-Italic-VariableFont_wght';
|
||||||
src: url(assets/fonts/WorkSans-Italic-VariableFont_wght.ttf) format('truetype');
|
src: url(assets/fonts/WorkSans-Italic-VariableFont_wght.ttf) format('truetype');
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "WorkSans-VariableFont_wght";
|
font-family: 'WorkSans-VariableFont_wght';
|
||||||
src: url(assets/fonts/WorkSans-VariableFont_wght.ttf) format('truetype');
|
src: url(assets/fonts/WorkSans-VariableFont_wght.ttf) format('truetype');
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.App {
|
.App {
|
||||||
font-family: "Helvetica Neue", HelveticaNeue, Helvetica, Arial, sans-serif;
|
font-family: 'Work Sans', 'Helvetica Neue', HelveticaNeue, Helvetica, Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
a, button {
|
a,
|
||||||
font-family: "IBMPlexMono500" !important;
|
button {
|
||||||
|
font-family: 'IBMPlexMono500' !important;
|
||||||
color: #dd7700;
|
color: #dd7700;
|
||||||
}
|
}
|
||||||
+1
-1
@@ -2,7 +2,7 @@ import CssBaseline from '@material-ui/core/CssBaseline'
|
|||||||
import { ThemeProvider } from '@material-ui/core/styles'
|
import { ThemeProvider } from '@material-ui/core/styles'
|
||||||
import { SnackbarProvider } from 'notistack'
|
import { SnackbarProvider } from 'notistack'
|
||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import { BrowserRouter as Router } from 'react-router-dom'
|
import { HashRouter as Router } from 'react-router-dom'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
import Dashboard from './layout/Dashboard'
|
import Dashboard from './layout/Dashboard'
|
||||||
import { Provider as BeeProvider } from './providers/Bee'
|
import { Provider as BeeProvider } from './providers/Bee'
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,7 +3,7 @@ import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
|
|||||||
import { ArrowForward, OpenInNewSharp } from '@material-ui/icons'
|
import { ArrowForward, OpenInNewSharp } from '@material-ui/icons'
|
||||||
import { ReactElement, useState } from 'react'
|
import { ReactElement, useState } from 'react'
|
||||||
import CopyToClipboard from 'react-copy-to-clipboard'
|
import CopyToClipboard from 'react-copy-to-clipboard'
|
||||||
import { useHistory } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -61,7 +61,7 @@ export default function ExpandableListItemLink({
|
|||||||
}: Props): ReactElement | null {
|
}: Props): ReactElement | null {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const tooltipClickHandler = () => setCopied(true)
|
const tooltipClickHandler = () => setCopied(true)
|
||||||
const tooltipCloseHandler = () => setCopied(false)
|
const tooltipCloseHandler = () => setCopied(false)
|
||||||
@@ -72,7 +72,7 @@ export default function ExpandableListItemLink({
|
|||||||
if (navigationType === 'NEW_WINDOW') {
|
if (navigationType === 'NEW_WINDOW') {
|
||||||
window.open(link || value)
|
window.open(link || value)
|
||||||
} else {
|
} else {
|
||||||
history.push(link || value)
|
navigate(link || value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
|
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
|
||||||
import { ArrowBack } from '@material-ui/icons'
|
import { ArrowBack } from '@material-ui/icons'
|
||||||
import { ReactElement } from 'react'
|
import { ReactElement } from 'react'
|
||||||
import { useHistory } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: string
|
children: string
|
||||||
@@ -20,10 +20,10 @@ const useStyles = makeStyles(() =>
|
|||||||
|
|
||||||
export function HistoryHeader({ children }: Props): ReactElement {
|
export function HistoryHeader({ children }: Props): ReactElement {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
function goBack() {
|
function goBack() {
|
||||||
history.goBack()
|
navigate(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ interface Props {
|
|||||||
export default function SideBarItem({ iconStart, iconEnd, path, label }: Props): ReactElement {
|
export default function SideBarItem({ iconStart, iconEnd, path, label }: Props): ReactElement {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const isSelected = Boolean(matchPath(location.pathname, { path, exact: true }))
|
const isSelected = Boolean(path && matchPath(location.pathname, path))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledListItem button selected={isSelected} disableRipple>
|
<StyledListItem button selected={isSelected} disableRipple>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default function SideBarItem({ path }: Props): ReactElement {
|
|||||||
const { status, isLoading } = useContext(Context)
|
const { status, isLoading } = useContext(Context)
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const isSelected = Boolean(matchPath(location.pathname, { path, exact: true }))
|
const isSelected = Boolean(path && matchPath(location.pathname, path))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
'& fieldset': {
|
'& fieldset': {
|
||||||
border: 0,
|
border: 0,
|
||||||
},
|
},
|
||||||
|
'& .MuiSelect-select': {
|
||||||
|
'&:focus': {
|
||||||
|
background: theme.palette.background.paper,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
option: {
|
option: {
|
||||||
height: '52px',
|
height: '52px',
|
||||||
@@ -48,6 +53,7 @@ export function SwarmSelect({ defaultValue, formik, name, options, onChange, lab
|
|||||||
defaultValue={defaultValue || ''}
|
defaultValue={defaultValue || ''}
|
||||||
className={classes.select}
|
className={classes.select}
|
||||||
placeholder={label}
|
placeholder={label}
|
||||||
|
MenuProps={{ MenuListProps: { disablePadding: true }, PaperProps: { square: true } }}
|
||||||
>
|
>
|
||||||
{options.map((x, i) => (
|
{options.map((x, i) => (
|
||||||
<MenuItem key={i} value={x.value} className={classes.option}>
|
<MenuItem key={i} value={x.value} className={classes.option}>
|
||||||
@@ -71,6 +77,7 @@ export function SwarmSelect({ defaultValue, formik, name, options, onChange, lab
|
|||||||
defaultValue={defaultValue || ''}
|
defaultValue={defaultValue || ''}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
placeholder={label}
|
placeholder={label}
|
||||||
|
MenuProps={{ MenuListProps: { disablePadding: true }, PaperProps: { square: true } }}
|
||||||
>
|
>
|
||||||
{options.map((x, i) => (
|
{options.map((x, i) => (
|
||||||
<MenuItem key={i} value={x.value} className={classes.option}>
|
<MenuItem key={i} value={x.value} className={classes.option}>
|
||||||
|
|||||||
@@ -16,10 +16,18 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
createStyles({
|
createStyles({
|
||||||
field: {
|
field: {
|
||||||
background: theme.palette.background.paper,
|
background: theme.palette.background.paper,
|
||||||
height: '52px',
|
|
||||||
'& fieldset': {
|
'& fieldset': {
|
||||||
border: 0,
|
border: 0,
|
||||||
},
|
},
|
||||||
|
'& .Mui-focused': {
|
||||||
|
background: theme.palette.background.paper,
|
||||||
|
},
|
||||||
|
'& .MuiInputBase-root': {
|
||||||
|
background: theme.palette.background.paper,
|
||||||
|
},
|
||||||
|
'& .MuiFilledInput-root': {
|
||||||
|
borderRadius: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -36,9 +44,10 @@ export function SwarmTextInput({ name, label, password, optional, formik, onChan
|
|||||||
name={name}
|
name={name}
|
||||||
label={label}
|
label={label}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="filled"
|
||||||
className={classes.field}
|
className={classes.field}
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
|
InputProps={{ disableUnderline: true }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -49,10 +58,11 @@ export function SwarmTextInput({ name, label, password, optional, formik, onChan
|
|||||||
required
|
required
|
||||||
label={label}
|
label={label}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="filled"
|
||||||
className={classes.field}
|
className={classes.field}
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
InputProps={{ disableUnderline: true }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const META_FILE_NAME = '.swarmgatewaymeta.json'
|
||||||
|
export const PREVIEW_FILE_NAME = '.swarmgatewaypreview.jpeg'
|
||||||
|
export const PREVIEW_DIMENSIONS = { maxWidth: 250, maxHeight: 175 }
|
||||||
@@ -3,7 +3,7 @@ import { Form, Formik } from 'formik'
|
|||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useState } from 'react'
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
import { Check, X } from 'react-feather'
|
import { Check, X } from 'react-feather'
|
||||||
import { useHistory } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
import { DocumentationText } from '../../components/DocumentationText'
|
import { DocumentationText } from '../../components/DocumentationText'
|
||||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||||
@@ -34,7 +34,7 @@ export default function CreateNewFeed(): ReactElement {
|
|||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
|
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
async function onSubmit(values: FormValues) {
|
async function onSubmit(values: FormValues) {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
@@ -65,12 +65,12 @@ export default function CreateNewFeed(): ReactElement {
|
|||||||
const identity = await convertWalletToIdentity(wallet, values.type, values.identityName, values.password)
|
const identity = await convertWalletToIdentity(wallet, values.type, values.identityName, values.password)
|
||||||
persistIdentity(identities, identity)
|
persistIdentity(identities, identity)
|
||||||
setIdentities(identities)
|
setIdentities(identities)
|
||||||
history.push(ROUTES.FEEDS)
|
navigate(ROUTES.FEEDS)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
history.goBack()
|
navigate(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as swarmCid from '@ethersphere/swarm-cid'
|
|||||||
import { Box } from '@material-ui/core'
|
import { Box } from '@material-ui/core'
|
||||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||||
import { X } from 'react-feather'
|
import { X } from 'react-feather'
|
||||||
import { RouteComponentProps, useHistory } from 'react-router-dom'
|
import { useParams, useNavigate } from 'react-router-dom'
|
||||||
import { DocumentationText } from '../../components/DocumentationText'
|
import { DocumentationText } from '../../components/DocumentationText'
|
||||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||||
@@ -15,20 +15,16 @@ import { Context as SettingsContext } from '../../providers/Settings'
|
|||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { UploadArea } from '../files/UploadArea'
|
import { UploadArea } from '../files/UploadArea'
|
||||||
|
|
||||||
interface MatchParams {
|
export function FeedSubpage(): ReactElement {
|
||||||
uuid: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FeedSubpage(props: RouteComponentProps<MatchParams>): ReactElement {
|
|
||||||
const { identities } = useContext(IdentityContext)
|
const { identities } = useContext(IdentityContext)
|
||||||
|
const { uuid } = useParams()
|
||||||
const { beeApi } = useContext(SettingsContext)
|
const { beeApi } = useContext(SettingsContext)
|
||||||
const { status } = useContext(BeeContext)
|
const { status } = useContext(BeeContext)
|
||||||
|
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const [available, setAvailable] = useState(false)
|
const [available, setAvailable] = useState(false)
|
||||||
|
|
||||||
const uuid = props.match.params.uuid
|
|
||||||
const identity = identities.find(x => x.uuid === uuid)
|
const identity = identities.find(x => x.uuid === uuid)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -44,13 +40,13 @@ export function FeedSubpage(props: RouteComponentProps<MatchParams>): ReactEleme
|
|||||||
}, [beeApi, uuid, identity])
|
}, [beeApi, uuid, identity])
|
||||||
|
|
||||||
if (!identity || !status.all) {
|
if (!identity || !status.all) {
|
||||||
history.replace(ROUTES.FEEDS)
|
navigate(ROUTES.FEEDS, { replace: true })
|
||||||
|
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClose() {
|
function onClose() {
|
||||||
history.push(ROUTES.FEEDS)
|
navigate(ROUTES.FEEDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Box, Grid, Typography } from '@material-ui/core'
|
|||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||||
import { Bookmark, X } from 'react-feather'
|
import { Bookmark, X } from 'react-feather'
|
||||||
import { RouteComponentProps, useHistory } from 'react-router'
|
import { useParams, useNavigate } from 'react-router'
|
||||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||||
import { SwarmButton } from '../../components/SwarmButton'
|
import { SwarmButton } from '../../components/SwarmButton'
|
||||||
@@ -16,15 +16,12 @@ import { ROUTES } from '../../routes'
|
|||||||
import { persistIdentity, updateFeed } from '../../utils/identity'
|
import { persistIdentity, updateFeed } from '../../utils/identity'
|
||||||
import { FeedPasswordDialog } from './FeedPasswordDialog'
|
import { FeedPasswordDialog } from './FeedPasswordDialog'
|
||||||
|
|
||||||
interface MatchParams {
|
export default function UpdateFeed(): ReactElement {
|
||||||
hash: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function UpdateFeed(props: RouteComponentProps<MatchParams>): ReactElement {
|
|
||||||
const { identities, setIdentities } = useContext(IdentityContext)
|
const { identities, setIdentities } = useContext(IdentityContext)
|
||||||
const { beeApi, beeDebugApi } = useContext(SettingsContext)
|
const { beeApi, beeDebugApi } = useContext(SettingsContext)
|
||||||
const { stamps, refresh } = useContext(StampContext)
|
const { stamps, refresh } = useContext(StampContext)
|
||||||
const { status } = useContext(BeeContext)
|
const { status } = useContext(BeeContext)
|
||||||
|
const { hash } = useParams()
|
||||||
|
|
||||||
const [selectedStamp, setSelectedStamp] = useState<string | null>(null)
|
const [selectedStamp, setSelectedStamp] = useState<string | null>(null)
|
||||||
const [selectedIdentity, setSelectedIdentity] = useState<Identity | null>(null)
|
const [selectedIdentity, setSelectedIdentity] = useState<Identity | null>(null)
|
||||||
@@ -32,7 +29,7 @@ export default function UpdateFeed(props: RouteComponentProps<MatchParams>): Rea
|
|||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
const [showPasswordPrompt, setShowPasswordPrompt] = useState(false)
|
const [showPasswordPrompt, setShowPasswordPrompt] = useState(false)
|
||||||
|
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refresh()
|
refresh()
|
||||||
@@ -50,7 +47,7 @@ export default function UpdateFeed(props: RouteComponentProps<MatchParams>): Rea
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onCancel() {
|
function onCancel() {
|
||||||
history.goBack()
|
navigate(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBeginUpdatingFeed() {
|
function onBeginUpdatingFeed() {
|
||||||
@@ -76,10 +73,10 @@ export default function UpdateFeed(props: RouteComponentProps<MatchParams>): Rea
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateFeed(beeApi, identity, props.match.params.hash, selectedStamp, password as string)
|
await updateFeed(beeApi, identity, hash!, selectedStamp, password as string) // eslint-disable-line
|
||||||
persistIdentity(identities, identity)
|
persistIdentity(identities, identity)
|
||||||
setIdentities([...identities])
|
setIdentities([...identities])
|
||||||
history.push(ROUTES.FEEDS_PAGE.replace(':uuid', identity.uuid))
|
navigate(ROUTES.FEEDS_PAGE.replace(':uuid', identity.uuid))
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { Box, Typography } from '@material-ui/core'
|
import { Box, Typography } from '@material-ui/core'
|
||||||
import { ReactElement, useContext, useState } from 'react'
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
import { Download, Info, PlusSquare, Trash } from 'react-feather'
|
import { Download, Info, PlusSquare, Trash } from 'react-feather'
|
||||||
import { useHistory } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
import ExpandableList from '../../components/ExpandableList'
|
import ExpandableList from '../../components/ExpandableList'
|
||||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||||
import { SwarmButton } from '../../components/SwarmButton'
|
import { SwarmButton } from '../../components/SwarmButton'
|
||||||
|
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||||
import { Context as BeeContext } from '../../providers/Bee'
|
import { Context as BeeContext } from '../../providers/Bee'
|
||||||
import { Context as IdentityContext, Identity } from '../../providers/Feeds'
|
import { Context as IdentityContext, Identity } from '../../providers/Feeds'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
@@ -20,7 +21,7 @@ export default function Feeds(): ReactElement {
|
|||||||
const { identities, setIdentities } = useContext(IdentityContext)
|
const { identities, setIdentities } = useContext(IdentityContext)
|
||||||
const { status } = useContext(BeeContext)
|
const { status } = useContext(BeeContext)
|
||||||
|
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const [selectedIdentity, setSelectedIdentity] = useState<Identity | null>(null)
|
const [selectedIdentity, setSelectedIdentity] = useState<Identity | null>(null)
|
||||||
const [showImport, setShowImport] = useState(false)
|
const [showImport, setShowImport] = useState(false)
|
||||||
@@ -28,11 +29,11 @@ export default function Feeds(): ReactElement {
|
|||||||
const [showDelete, setShowDelete] = useState(false)
|
const [showDelete, setShowDelete] = useState(false)
|
||||||
|
|
||||||
function createNewFeed() {
|
function createNewFeed() {
|
||||||
return history.push(ROUTES.FEEDS_NEW)
|
return navigate(ROUTES.FEEDS_NEW)
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewFeed(uuid: string) {
|
function viewFeed(uuid: string) {
|
||||||
history.push(ROUTES.FEEDS_PAGE.replace(':uuid', uuid))
|
navigate(ROUTES.FEEDS_PAGE.replace(':uuid', uuid))
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDialogClose() {
|
function onDialogClose() {
|
||||||
@@ -59,6 +60,8 @@ export default function Feeds(): ReactElement {
|
|||||||
setShowDelete(true)
|
setShowDelete(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!status.all) return <TroubleshootConnectionCard />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{showImport && <ImportFeedDialog onClose={() => setShowImport(false)} />}
|
{showImport && <ImportFeedDialog onClose={() => setShowImport(false)} />}
|
||||||
@@ -95,11 +98,9 @@ export default function Feeds(): ReactElement {
|
|||||||
{x.feedHash && <ExpandableListItemKey label="Feed hash" value={x.feedHash} />}
|
{x.feedHash && <ExpandableListItemKey label="Feed hash" value={x.feedHash} />}
|
||||||
<Box mt={0.75}>
|
<Box mt={0.75}>
|
||||||
<ExpandableListItemActions>
|
<ExpandableListItemActions>
|
||||||
{status.all && (
|
|
||||||
<SwarmButton onClick={() => viewFeed(x.uuid)} iconType={Info}>
|
<SwarmButton onClick={() => viewFeed(x.uuid)} iconType={Info}>
|
||||||
View Feed Page
|
View Feed Page
|
||||||
</SwarmButton>
|
</SwarmButton>
|
||||||
)}
|
|
||||||
<SwarmButton onClick={() => onShowExport(x)} iconType={Download}>
|
<SwarmButton onClick={() => onShowExport(x)} iconType={Download}>
|
||||||
Export...
|
Export...
|
||||||
</SwarmButton>
|
</SwarmButton>
|
||||||
|
|||||||
@@ -1,99 +1,58 @@
|
|||||||
import { Box, Grid, Typography } from '@material-ui/core'
|
import { Box, Grid, Typography } from '@material-ui/core'
|
||||||
import { Web } from '@material-ui/icons'
|
import { Web } from '@material-ui/icons'
|
||||||
import { ReactElement, useEffect, useState } from 'react'
|
import { ReactElement } from 'react'
|
||||||
import { File, Folder } from 'react-feather'
|
import { File, Folder } from 'react-feather'
|
||||||
import { FitImage } from '../../components/FitImage'
|
import { FitImage } from '../../components/FitImage'
|
||||||
import { detectIndexHtml, getAssetNameFromFiles, getHumanReadableFileSize } from '../../utils/file'
|
import { shortenText } from '../../utils'
|
||||||
import { SwarmFile } from '../../utils/SwarmFile'
|
import { getHumanReadableFileSize } from '../../utils/file'
|
||||||
|
import { shortenHash } from '../../utils/hash'
|
||||||
import { AssetIcon } from './AssetIcon'
|
import { AssetIcon } from './AssetIcon'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
assetName?: string
|
previewUri?: string
|
||||||
files: SwarmFile[]
|
metadata?: Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add optional prop for indexDocument when it is already known (e.g. downloading a manifest)
|
// TODO: add optional prop for indexDocument when it is already known (e.g. downloading a manifest)
|
||||||
|
|
||||||
export function AssetPreview({ assetName, files }: Props): ReactElement {
|
export function AssetPreview({ metadata, previewUri }: Props): ReactElement | null {
|
||||||
const [previewComponent, setPreviewComponent] = useState<ReactElement | undefined>(undefined)
|
let previewComponent = <File />
|
||||||
const [previewUri, setPreviewUri] = useState<string | undefined>(undefined)
|
let type = metadata?.type
|
||||||
|
|
||||||
useEffect(() => {
|
if (metadata?.isWebsite) {
|
||||||
if (files.length === 1) {
|
previewComponent = <Web />
|
||||||
// single image
|
type = 'Website'
|
||||||
if (files[0].type.startsWith('image/')) {
|
} else if (metadata?.type === 'folder') {
|
||||||
files[0].arrayBuffer().then(value => {
|
previewComponent = <Folder />
|
||||||
const blob = new Blob([value])
|
type = 'Folder'
|
||||||
setPreviewUri(URL.createObjectURL(blob))
|
|
||||||
})
|
|
||||||
// single non-image
|
|
||||||
} else {
|
|
||||||
setPreviewUri(undefined)
|
|
||||||
setPreviewComponent(<AssetIcon icon={<File />} />)
|
|
||||||
}
|
}
|
||||||
// collection
|
|
||||||
} else if (detectIndexHtml(files)) {
|
|
||||||
setPreviewUri(undefined)
|
|
||||||
setPreviewComponent(<AssetIcon icon={<Web />} />)
|
|
||||||
} else {
|
|
||||||
setPreviewUri(undefined)
|
|
||||||
setPreviewComponent(<AssetIcon icon={<Folder />} />)
|
|
||||||
}
|
|
||||||
}, [files])
|
|
||||||
|
|
||||||
const getPrimaryText = () => {
|
|
||||||
const name = getAssetNameFromFiles(files)
|
|
||||||
|
|
||||||
if (files.length === 1) {
|
|
||||||
return 'Filename: ' + (assetName || name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Folder name: ' + (assetName || name)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getKind = () => {
|
|
||||||
if (files.length === 1) {
|
|
||||||
return files[0].type
|
|
||||||
}
|
|
||||||
|
|
||||||
if (detectIndexHtml(files)) {
|
|
||||||
return 'Website'
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Folder'
|
|
||||||
}
|
|
||||||
|
|
||||||
const isFolder = () => ['Folder', 'Website'].includes(getKind())
|
|
||||||
|
|
||||||
const getSize = () => {
|
|
||||||
const bytes = files.reduce((total, item) => total + item.size, 0)
|
|
||||||
|
|
||||||
return getHumanReadableFileSize(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
const size = getSize()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<Box bgcolor="background.paper">
|
<Box bgcolor="background.paper">
|
||||||
<Grid container direction="row">
|
<Grid container direction="row">
|
||||||
{previewComponent ? (
|
{previewUri ? (
|
||||||
previewComponent
|
|
||||||
) : (
|
|
||||||
<FitImage maxWidth="250px" maxHeight="175px" alt="Upload Preview" src={previewUri} />
|
<FitImage maxWidth="250px" maxHeight="175px" alt="Upload Preview" src={previewUri} />
|
||||||
|
) : (
|
||||||
|
<AssetIcon icon={previewComponent} />
|
||||||
)}
|
)}
|
||||||
<Box p={2}>
|
<Box p={2}>
|
||||||
<Typography>{getPrimaryText()}</Typography>
|
{metadata?.hash && <Typography>Swarm Hash: {shortenHash(metadata.hash)}</Typography>}
|
||||||
<Typography>Kind: {getKind()}</Typography>
|
{metadata?.name && metadata?.name !== metadata?.hash && (
|
||||||
{size !== '0 bytes' && <Typography>Size: {size}</Typography>}
|
<Typography>
|
||||||
|
{metadata?.type === 'folder' ? 'Folder Name' : 'Filename'}: {shortenText(metadata?.name)}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
<Typography>Kind: {type}</Typography>
|
||||||
|
{metadata?.size ? <Typography>Size: {getHumanReadableFileSize(metadata.size)}</Typography> : null}
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
{isFolder() && (
|
{metadata?.type === 'folder' && metadata.count && (
|
||||||
<Box mt={0.25} p={2} bgcolor="background.paper">
|
<Box mt={0.25} p={2} bgcolor="background.paper">
|
||||||
<Grid container justifyContent="space-between" alignItems="center" direction="row">
|
<Grid container justifyContent="space-between" alignItems="center" direction="row">
|
||||||
<Typography variant="subtitle2">Folder content</Typography>
|
<Typography variant="subtitle2">Folder content</Typography>
|
||||||
<Typography variant="subtitle2">{files.length} items</Typography>
|
<Typography variant="subtitle2">{metadata.count} items</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -4,21 +4,19 @@ import { ReactElement } from 'react'
|
|||||||
import { DocumentationText } from '../../components/DocumentationText'
|
import { DocumentationText } from '../../components/DocumentationText'
|
||||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||||
import ExpandableListItemLink from '../../components/ExpandableListItemLink'
|
import ExpandableListItemLink from '../../components/ExpandableListItemLink'
|
||||||
import { detectIndexHtml } from '../../utils/file'
|
|
||||||
import { SwarmFile } from '../../utils/SwarmFile'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
files: SwarmFile[]
|
isWebsite?: boolean
|
||||||
hash: string
|
hash: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AssetSummary({ files, hash }: Props): ReactElement {
|
export function AssetSummary({ isWebsite, hash }: Props): ReactElement {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<ExpandableListItemKey label="Swarm hash" value={hash} />
|
<ExpandableListItemKey label="Swarm hash" value={hash} />
|
||||||
<ExpandableListItemLink label="Share on Swarm Gateway" value={`https://gateway.ethswarm.org/access/${hash}`} />
|
<ExpandableListItemLink label="Share on Swarm Gateway" value={`https://gateway.ethswarm.org/access/${hash}`} />
|
||||||
{detectIndexHtml(files) && (
|
{isWebsite && (
|
||||||
<ExpandableListItemLink
|
<ExpandableListItemLink
|
||||||
label="BZZ Link"
|
label="BZZ Link"
|
||||||
value={`https://${swarmCid.encodeManifestReference(hash).toString()}.bzz.link`}
|
value={`https://${swarmCid.encodeManifestReference(hash).toString()}.bzz.link`}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Utils } from '@ethersphere/bee-js'
|
|||||||
import { ManifestJs } from '@ethersphere/manifest-js'
|
import { ManifestJs } from '@ethersphere/manifest-js'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useState } from 'react'
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
import { useHistory } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import ExpandableListItemInput from '../../components/ExpandableListItemInput'
|
import ExpandableListItemInput from '../../components/ExpandableListItemInput'
|
||||||
import { History } from '../../components/History'
|
import { History } from '../../components/History'
|
||||||
import { Context, defaultUploadOrigin } from '../../providers/File'
|
import { Context, defaultUploadOrigin } from '../../providers/File'
|
||||||
@@ -20,7 +20,7 @@ export function Download(): ReactElement {
|
|||||||
const { setUploadOrigin } = useContext(Context)
|
const { setUploadOrigin } = useContext(Context)
|
||||||
|
|
||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const validateChange = (value: string) => {
|
const validateChange = (value: string) => {
|
||||||
if (Utils.isHexString(value, 64) || Utils.isHexString(value, 128) || !value.trim().length) {
|
if (Utils.isHexString(value, 64) || Utils.isHexString(value, 128) || !value.trim().length) {
|
||||||
@@ -54,7 +54,7 @@ export function Download(): ReactElement {
|
|||||||
const indexDocument = await manifestJs.getIndexDocumentPath(identifier)
|
const indexDocument = await manifestJs.getIndexDocumentPath(identifier)
|
||||||
putHistory(HISTORY_KEYS.DOWNLOAD_HISTORY, identifier, determineHistoryName(identifier, indexDocument))
|
putHistory(HISTORY_KEYS.DOWNLOAD_HISTORY, identifier, determineHistoryName(identifier, indexDocument))
|
||||||
setUploadOrigin(defaultUploadOrigin)
|
setUploadOrigin(defaultUploadOrigin)
|
||||||
history.push(ROUTES.HASH.replace(':hash', identifier))
|
navigate(ROUTES.HASH.replace(':hash', identifier))
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
let message = typeof error === 'object' && error !== null && Reflect.get(error, 'message')
|
let message = typeof error === 'object' && error !== null && Reflect.get(error, 'message')
|
||||||
|
|
||||||
|
|||||||
@@ -32,12 +32,12 @@ export function DownloadActionBar({
|
|||||||
<SwarmButton onClick={onDownload} iconType={Download} disabled={loading} loading={loading}>
|
<SwarmButton onClick={onDownload} iconType={Download} disabled={loading} loading={loading}>
|
||||||
Download
|
Download
|
||||||
</SwarmButton>
|
</SwarmButton>
|
||||||
<SwarmButton onClick={onCancel} iconType={X} disabled={loading} loading={loading} cancel>
|
<SwarmButton onClick={onCancel} iconType={X} disabled={loading} cancel>
|
||||||
Close
|
Close
|
||||||
</SwarmButton>
|
</SwarmButton>
|
||||||
</ExpandableListItemActions>
|
</ExpandableListItemActions>
|
||||||
<Box mb={1} mr={1}>
|
<Box mb={1} mr={1}>
|
||||||
<SwarmButton onClick={onUpdateFeed} iconType={Bookmark}>
|
<SwarmButton onClick={onUpdateFeed} iconType={Bookmark} disabled={loading}>
|
||||||
Update Feed
|
Update Feed
|
||||||
</SwarmButton>
|
</SwarmButton>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createStyles, makeStyles, Tab, Tabs, Theme } from '@material-ui/core'
|
import { createStyles, makeStyles, Tab, Tabs, Theme } from '@material-ui/core'
|
||||||
import { ReactElement } from 'react'
|
import { ReactElement } from 'react'
|
||||||
import { useHistory } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -24,10 +24,10 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
|
|
||||||
export function FileNavigation({ active }: Props): ReactElement {
|
export function FileNavigation({ active }: Props): ReactElement {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<Record<string, never>>, newValue: number) {
|
function onChange(event: React.ChangeEvent<Record<string, never>>, newValue: number) {
|
||||||
history.push(newValue === 1 ? ROUTES.DOWNLOAD : ROUTES.UPLOAD)
|
navigate(newValue === 1 ? ROUTES.DOWNLOAD : ROUTES.UPLOAD)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+45
-28
@@ -4,40 +4,37 @@ import { saveAs } from 'file-saver'
|
|||||||
import JSZip from 'jszip'
|
import JSZip from 'jszip'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||||
import { RouteComponentProps, useHistory } from 'react-router-dom'
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||||
import { Loading } from '../../components/Loading'
|
import { Loading } from '../../components/Loading'
|
||||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||||
|
import config from '../../config'
|
||||||
|
import { META_FILE_NAME, PREVIEW_FILE_NAME } from '../../constants'
|
||||||
import { Context as BeeContext } from '../../providers/Bee'
|
import { Context as BeeContext } from '../../providers/Bee'
|
||||||
import { Context as SettingsContext } from '../../providers/Settings'
|
import { Context as SettingsContext } from '../../providers/Settings'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { convertBeeFileToBrowserFile, convertManifestToFiles } from '../../utils/file'
|
|
||||||
import { shortenHash } from '../../utils/hash'
|
|
||||||
import { determineHistoryName, HISTORY_KEYS, putHistory } from '../../utils/local-storage'
|
import { determineHistoryName, HISTORY_KEYS, putHistory } from '../../utils/local-storage'
|
||||||
import { SwarmFile } from '../../utils/SwarmFile'
|
|
||||||
import { AssetPreview } from './AssetPreview'
|
import { AssetPreview } from './AssetPreview'
|
||||||
import { AssetSummary } from './AssetSummary'
|
import { AssetSummary } from './AssetSummary'
|
||||||
import { DownloadActionBar } from './DownloadActionBar'
|
import { DownloadActionBar } from './DownloadActionBar'
|
||||||
|
|
||||||
interface MatchParams {
|
export function Share(): ReactElement {
|
||||||
hash: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Share(props: RouteComponentProps<MatchParams>): ReactElement {
|
|
||||||
const { apiUrl, beeApi } = useContext(SettingsContext)
|
const { apiUrl, beeApi } = useContext(SettingsContext)
|
||||||
const { status } = useContext(BeeContext)
|
const { status } = useContext(BeeContext)
|
||||||
|
|
||||||
const reference = props.match.params.hash
|
const { hash } = useParams()
|
||||||
|
const reference = hash! // eslint-disable-line
|
||||||
|
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
|
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [downloading, setDownloading] = useState(false)
|
const [downloading, setDownloading] = useState(false)
|
||||||
const [files, setFiles] = useState<SwarmFile[]>([])
|
|
||||||
const [swarmEntries, setSwarmEntries] = useState<Record<string, string>>({})
|
const [swarmEntries, setSwarmEntries] = useState<Record<string, string>>({})
|
||||||
const [indexDocument, setIndexDocument] = useState<string | null>(null)
|
const [indexDocument, setIndexDocument] = useState<string | null>(null)
|
||||||
const [notFound, setNotFound] = useState(false)
|
const [notFound, setNotFound] = useState(false)
|
||||||
|
const [preview, setPreview] = useState<string | undefined>(undefined)
|
||||||
|
const [metadata, setMetadata] = useState<Metadata | undefined>()
|
||||||
|
|
||||||
async function prepare() {
|
async function prepare() {
|
||||||
if (!beeApi || !status.all) {
|
if (!beeApi || !status.all) {
|
||||||
@@ -54,16 +51,37 @@ export function Share(props: RouteComponentProps<MatchParams>): ReactElement {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const entries = await manifestJs.getHashes(reference)
|
const entries = await manifestJs.getHashes(reference)
|
||||||
setSwarmEntries(entries)
|
|
||||||
const indexDocument = await manifestJs.getIndexDocumentPath(reference)
|
const indexDocument = await manifestJs.getIndexDocumentPath(reference)
|
||||||
setIndexDocument(indexDocument)
|
setIndexDocument(indexDocument)
|
||||||
|
|
||||||
if (Object.keys(entries).length === 1) {
|
const previewFile = entries[PREVIEW_FILE_NAME]
|
||||||
const response = await beeApi.downloadFile(reference)
|
|
||||||
setFiles([new SwarmFile(convertBeeFileToBrowserFile(response) as File)])
|
delete entries[META_FILE_NAME]
|
||||||
} else {
|
delete entries[PREVIEW_FILE_NAME]
|
||||||
setFiles(convertManifestToFiles(entries))
|
setSwarmEntries(entries)
|
||||||
|
|
||||||
|
const count = Object.keys(entries).length
|
||||||
|
|
||||||
|
let metadata: Metadata | undefined = {
|
||||||
|
hash,
|
||||||
|
size: 0,
|
||||||
|
type: count > 1 ? 'folder' : 'unknown',
|
||||||
|
name: reference,
|
||||||
|
isWebsite: Boolean(indexDocument) && count > 1,
|
||||||
|
count,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const mtdt = await beeApi.downloadFile(reference, META_FILE_NAME)
|
||||||
|
const remoteMetadata = mtdt.data.text()
|
||||||
|
metadata = { ...metadata, ...(JSON.parse(remoteMetadata) as Metadata) }
|
||||||
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
|
|
||||||
|
if (previewFile) {
|
||||||
|
setPreview(`${config.BEE_API_HOST}/bzz/${reference}/${PREVIEW_FILE_NAME}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
setMetadata(metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onOpen() {
|
function onOpen() {
|
||||||
@@ -71,16 +89,17 @@ export function Share(props: RouteComponentProps<MatchParams>): ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onClose() {
|
function onClose() {
|
||||||
// POP means there is no history - nowhere to go back yet
|
if (navigate.length > 0) {
|
||||||
if (history.action === 'POP') {
|
// There is at least one different route in browser history that we can return to
|
||||||
history.push(ROUTES.UPLOAD)
|
navigate(-1)
|
||||||
} else {
|
} else {
|
||||||
history.goBack()
|
// This is the first page user opened, navigate to upload page instead of going back
|
||||||
|
navigate(ROUTES.UPLOAD)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onUpdateFeed() {
|
function onUpdateFeed() {
|
||||||
history.push(ROUTES.FEEDS_UPDATE.replace(':hash', reference))
|
navigate(ROUTES.FEEDS_UPDATE.replace(':hash', reference))
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -111,8 +130,6 @@ export function Share(props: RouteComponentProps<MatchParams>): ReactElement {
|
|||||||
setDownloading(false)
|
setDownloading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const assetName = shortenHash(reference)
|
|
||||||
|
|
||||||
if (!status.all) return <TroubleshootConnectionCard />
|
if (!status.all) return <TroubleshootConnectionCard />
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@@ -131,17 +148,17 @@ export function Share(props: RouteComponentProps<MatchParams>): ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<AssetPreview files={files} assetName={assetName} />
|
<AssetPreview metadata={metadata} previewUri={preview} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<AssetSummary files={files} hash={reference} />
|
<AssetSummary isWebsite={metadata?.isWebsite} hash={reference} />
|
||||||
</Box>
|
</Box>
|
||||||
<DownloadActionBar
|
<DownloadActionBar
|
||||||
onOpen={onOpen}
|
onOpen={onOpen}
|
||||||
onCancel={onClose}
|
onCancel={onClose}
|
||||||
onDownload={onDownload}
|
onDownload={onDownload}
|
||||||
onUpdateFeed={onUpdateFeed}
|
onUpdateFeed={onUpdateFeed}
|
||||||
hasIndexDocument={Boolean(indexDocument && files.length > 1)}
|
hasIndexDocument={Boolean(metadata?.isWebsite)}
|
||||||
loading={downloading}
|
loading={downloading}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
+59
-11
@@ -1,7 +1,7 @@
|
|||||||
import { Box } from '@material-ui/core'
|
import { Box } from '@material-ui/core'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||||
import { useHistory } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { DocumentationText } from '../../components/DocumentationText'
|
import { DocumentationText } from '../../components/DocumentationText'
|
||||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||||
import { ProgressIndicator } from '../../components/ProgressIndicator'
|
import { ProgressIndicator } from '../../components/ProgressIndicator'
|
||||||
@@ -12,7 +12,7 @@ import { Context as FileContext } from '../../providers/File'
|
|||||||
import { Context as SettingsContext } from '../../providers/Settings'
|
import { Context as SettingsContext } from '../../providers/Settings'
|
||||||
import { Context as StampsContext, EnrichedPostageBatch } from '../../providers/Stamps'
|
import { Context as StampsContext, EnrichedPostageBatch } from '../../providers/Stamps'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { detectIndexHtml, getAssetNameFromFiles } from '../../utils/file'
|
import { detectIndexHtml, getAssetNameFromFiles, packageFile } from '../../utils/file'
|
||||||
import { persistIdentity, updateFeed } from '../../utils/identity'
|
import { persistIdentity, updateFeed } from '../../utils/identity'
|
||||||
import { HISTORY_KEYS, putHistory } from '../../utils/local-storage'
|
import { HISTORY_KEYS, putHistory } from '../../utils/local-storage'
|
||||||
import { FeedPasswordDialog } from '../feeds/FeedPasswordDialog'
|
import { FeedPasswordDialog } from '../feeds/FeedPasswordDialog'
|
||||||
@@ -21,6 +21,7 @@ import { PostageStampSelector } from '../stamps/PostageStampSelector'
|
|||||||
import { AssetPreview } from './AssetPreview'
|
import { AssetPreview } from './AssetPreview'
|
||||||
import { StampPreview } from './StampPreview'
|
import { StampPreview } from './StampPreview'
|
||||||
import { UploadActionBar } from './UploadActionBar'
|
import { UploadActionBar } from './UploadActionBar'
|
||||||
|
import { META_FILE_NAME, PREVIEW_FILE_NAME } from '../../constants'
|
||||||
|
|
||||||
export function Upload(): ReactElement {
|
export function Upload(): ReactElement {
|
||||||
const [step, setStep] = useState(0)
|
const [step, setStep] = useState(0)
|
||||||
@@ -31,12 +32,12 @@ export function Upload(): ReactElement {
|
|||||||
|
|
||||||
const { refresh } = useContext(StampsContext)
|
const { refresh } = useContext(StampsContext)
|
||||||
const { beeApi } = useContext(SettingsContext)
|
const { beeApi } = useContext(SettingsContext)
|
||||||
const { files, setFiles, uploadOrigin } = useContext(FileContext)
|
const { files, setFiles, uploadOrigin, metadata, previewUri, previewBlob } = useContext(FileContext)
|
||||||
const { identities, setIdentities } = useContext(IdentityContext)
|
const { identities, setIdentities } = useContext(IdentityContext)
|
||||||
const { status } = useContext(BeeContext)
|
const { status } = useContext(BeeContext)
|
||||||
|
|
||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refresh()
|
refresh()
|
||||||
@@ -46,7 +47,7 @@ export function Upload(): ReactElement {
|
|||||||
|
|
||||||
if (!files.length) {
|
if (!files.length) {
|
||||||
setFiles([])
|
setFiles([])
|
||||||
history.replace(ROUTES.UPLOAD)
|
navigate(ROUTES.UPLOAD, { replace: true })
|
||||||
|
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
@@ -66,26 +67,73 @@ export function Upload(): ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uploadFiles = (password?: string) => {
|
const uploadFiles = (password?: string) => {
|
||||||
if (!beeApi || !files.length || !stamp) {
|
if (!beeApi || !files.length || !stamp || !metadata) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const indexDocument = files.length === 1 ? files[0].name : detectIndexHtml(files) || undefined
|
let fls = files.map(packageFile) // Apart from packaging, this is needed to not modify the original files array as it can trigger effects
|
||||||
|
let indexDocument: string | undefined = undefined // This means we assume it's folder
|
||||||
|
|
||||||
|
if (files.length === 1) indexDocument = files[0].name
|
||||||
|
else if (files.length > 1) {
|
||||||
|
const idx = detectIndexHtml(files)
|
||||||
|
|
||||||
|
// This is a website
|
||||||
|
if (idx) {
|
||||||
|
// The website is in some directory, remove it
|
||||||
|
if (idx.commonPrefix) {
|
||||||
|
const substrStart = idx.commonPrefix.length
|
||||||
|
indexDocument = idx.indexPath.substr(substrStart)
|
||||||
|
fls = fls.map(f => {
|
||||||
|
const path = (f.path as string).substr(substrStart)
|
||||||
|
|
||||||
|
return { ...f, path, webkitRelativePath: path, fullPath: path }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// The website is not packed in a directory
|
||||||
|
indexDocument = idx.indexPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const lastModified = files[0].lastModified
|
||||||
|
|
||||||
|
// We want to store only some metadata
|
||||||
|
const mtd: SwarmMetadata = {
|
||||||
|
name: metadata.name,
|
||||||
|
size: metadata.size,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type of the file only makes sense for a single file
|
||||||
|
if (files.length === 1) mtd.type = metadata.type
|
||||||
|
|
||||||
|
const metafile = new File([JSON.stringify(mtd)], META_FILE_NAME, {
|
||||||
|
type: 'application/json',
|
||||||
|
lastModified,
|
||||||
|
})
|
||||||
|
fls.push(packageFile(metafile))
|
||||||
|
|
||||||
|
if (previewBlob) {
|
||||||
|
const previewFile = new File([previewBlob], PREVIEW_FILE_NAME, {
|
||||||
|
type: 'image/jpeg',
|
||||||
|
lastModified,
|
||||||
|
})
|
||||||
|
fls.push(packageFile(previewFile))
|
||||||
|
}
|
||||||
|
|
||||||
setUploading(true)
|
setUploading(true)
|
||||||
|
|
||||||
beeApi
|
beeApi
|
||||||
.uploadFiles(stamp.batchID, files as unknown as File[], { indexDocument })
|
.uploadFiles(stamp.batchID, fls, { indexDocument })
|
||||||
.then(hash => {
|
.then(hash => {
|
||||||
putHistory(HISTORY_KEYS.UPLOAD_HISTORY, hash.reference, getAssetNameFromFiles(files))
|
putHistory(HISTORY_KEYS.UPLOAD_HISTORY, hash.reference, getAssetNameFromFiles(files))
|
||||||
|
|
||||||
if (uploadOrigin.origin === 'UPLOAD') {
|
if (uploadOrigin.origin === 'UPLOAD') {
|
||||||
history.replace(ROUTES.HASH.replace(':hash', hash.reference))
|
navigate(ROUTES.HASH.replace(':hash', hash.reference), { replace: true })
|
||||||
} else {
|
} else {
|
||||||
updateFeed(beeApi, identity as Identity, hash.reference, stamp.batchID, password as string).then(() => {
|
updateFeed(beeApi, identity as Identity, hash.reference, stamp.batchID, password as string).then(() => {
|
||||||
persistIdentity(identities, identity as Identity)
|
persistIdentity(identities, identity as Identity)
|
||||||
setIdentities([...identities])
|
setIdentities([...identities])
|
||||||
history.replace(ROUTES.FEEDS_PAGE.replace(':uuid', uploadOrigin.uuid as string))
|
navigate(ROUTES.FEEDS_PAGE.replace(':uuid', uploadOrigin.uuid as string), { replace: true })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -121,7 +169,7 @@ export function Upload(): ReactElement {
|
|||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<ProgressIndicator steps={['Preview', 'Add postage stamp', 'Upload to node']} index={step} />
|
<ProgressIndicator steps={['Preview', 'Add postage stamp', 'Upload to node']} index={step} />
|
||||||
</Box>
|
</Box>
|
||||||
{(step === 0 || step === 2) && <AssetPreview files={files} />}
|
{(step === 0 || step === 2) && <AssetPreview metadata={metadata} previewUri={previewUri} />}
|
||||||
{step === 1 && (
|
{step === 1 && (
|
||||||
<>
|
<>
|
||||||
<Box mb={2}>
|
<Box mb={2}>
|
||||||
|
|||||||
@@ -3,13 +3,12 @@ import { DropzoneArea } from 'material-ui-dropzone'
|
|||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useState } from 'react'
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
import { FilePlus, FolderPlus, PlusCircle } from 'react-feather'
|
import { FilePlus, FolderPlus, PlusCircle } from 'react-feather'
|
||||||
import { useHistory } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { DocumentationText } from '../../components/DocumentationText'
|
import { DocumentationText } from '../../components/DocumentationText'
|
||||||
import { SwarmButton } from '../../components/SwarmButton'
|
import { SwarmButton } from '../../components/SwarmButton'
|
||||||
import { Context, UploadOrigin } from '../../providers/File'
|
import { Context, UploadOrigin } from '../../providers/File'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { detectIndexHtml } from '../../utils/file'
|
import { detectIndexHtml } from '../../utils/file'
|
||||||
import { SwarmFile } from '../../utils/SwarmFile'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
uploadOrigin: UploadOrigin
|
uploadOrigin: UploadOrigin
|
||||||
@@ -51,7 +50,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
export function UploadArea({ uploadOrigin, showHelp }: Props): ReactElement {
|
export function UploadArea({ uploadOrigin, showHelp }: Props): ReactElement {
|
||||||
const { setFiles, setUploadOrigin } = useContext(Context)
|
const { setFiles, setUploadOrigin } = useContext(Context)
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
const [strictWebsiteMode, setStrictWebsiteMode] = useState(false)
|
const [strictWebsiteMode, setStrictWebsiteMode] = useState(false)
|
||||||
const [version, setVersion] = useState(0)
|
const [version, setVersion] = useState(0)
|
||||||
@@ -99,8 +98,8 @@ export function UploadArea({ uploadOrigin, showHelp }: Props): ReactElement {
|
|||||||
|
|
||||||
const handleChange = (files?: File[]) => {
|
const handleChange = (files?: File[]) => {
|
||||||
if (files) {
|
if (files) {
|
||||||
const swarmFiles = files.map(x => new SwarmFile(x))
|
const FilePaths = files as FilePath[]
|
||||||
const indexDocument = files.length === 1 ? files[0].name : detectIndexHtml(swarmFiles) || undefined
|
const indexDocument = files.length === 1 ? files[0].name : detectIndexHtml(FilePaths) || undefined
|
||||||
|
|
||||||
if (files.length && strictWebsiteMode && !indexDocument) {
|
if (files.length && strictWebsiteMode && !indexDocument) {
|
||||||
enqueueSnackbar('To upload a website, there must be an index.html or index.htm in the root of the folder.', {
|
enqueueSnackbar('To upload a website, there must be an index.html or index.htm in the root of the folder.', {
|
||||||
@@ -111,11 +110,11 @@ export function UploadArea({ uploadOrigin, showHelp }: Props): ReactElement {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setFiles(swarmFiles)
|
setFiles(FilePaths)
|
||||||
|
|
||||||
if (files.length) {
|
if (files.length) {
|
||||||
setUploadOrigin(uploadOrigin)
|
setUploadOrigin(uploadOrigin)
|
||||||
history.push(ROUTES.UPLOAD_IN_PROGRESS)
|
navigate(ROUTES.UPLOAD_IN_PROGRESS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { ReactElement } from 'react'
|
import { ReactElement } from 'react'
|
||||||
import { useHistory } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { PostageStampCreation } from './PostageStampCreation'
|
import { PostageStampCreation } from './PostageStampCreation'
|
||||||
|
|
||||||
export function CreatePostageStampPage(): ReactElement {
|
export function CreatePostageStampPage(): ReactElement {
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
function onFinished() {
|
function onFinished() {
|
||||||
history.push(ROUTES.STAMPS)
|
navigate(ROUTES.STAMPS)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ import React, { ReactElement, useContext } from 'react'
|
|||||||
import { Check } from 'react-feather'
|
import { Check } from 'react-feather'
|
||||||
import { SwarmButton } from '../../components/SwarmButton'
|
import { SwarmButton } from '../../components/SwarmButton'
|
||||||
import { SwarmTextInput } from '../../components/SwarmTextInput'
|
import { SwarmTextInput } from '../../components/SwarmTextInput'
|
||||||
|
import { Context as BeeContext } from '../../providers/Bee'
|
||||||
import { Context as SettingsContext } from '../../providers/Settings'
|
import { Context as SettingsContext } from '../../providers/Settings'
|
||||||
import { Context } from '../../providers/Stamps'
|
import { Context as StampsContext } from '../../providers/Stamps'
|
||||||
import {
|
import {
|
||||||
calculateStampPrice,
|
calculateStampPrice,
|
||||||
convertAmountToSeconds,
|
convertAmountToSeconds,
|
||||||
@@ -34,8 +35,10 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function PostageStampCreation({ onFinished }: Props): ReactElement {
|
export function PostageStampCreation({ onFinished }: Props): ReactElement {
|
||||||
const { refresh } = useContext(Context)
|
const { chainState } = useContext(BeeContext)
|
||||||
|
const { refresh } = useContext(StampsContext)
|
||||||
const { beeDebugApi } = useContext(SettingsContext)
|
const { beeDebugApi } = useContext(SettingsContext)
|
||||||
|
|
||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
|
|
||||||
function getFileSize(depth: number): string {
|
function getFileSize(depth: number): string {
|
||||||
@@ -55,10 +58,14 @@ export function PostageStampCreation({ onFinished }: Props): ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getPrice(depth: number, amount: number): string {
|
function getPrice(depth: number, amount: number): string {
|
||||||
if (isNaN(amount) || amount <= 0 || isNaN(depth) || depth < 17 || depth > 255) {
|
const hasInvalidInput = isNaN(amount) || amount <= 0 || isNaN(depth) || depth < 17 || depth > 255
|
||||||
|
const isCurrentPriceAvailable = chainState && chainState.currentPrice
|
||||||
|
|
||||||
|
if (hasInvalidInput || !isCurrentPriceAvailable) {
|
||||||
return '-'
|
return '-'
|
||||||
}
|
}
|
||||||
const price = calculateStampPrice(depth, amount)
|
|
||||||
|
const price = calculateStampPrice(depth, amount, chainState.currentPrice)
|
||||||
|
|
||||||
return `${formatBzz(price)} BZZ`
|
return `${formatBzz(price)} BZZ`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { CircularProgress, Container } from '@material-ui/core'
|
|||||||
import { createStyles, makeStyles } from '@material-ui/core/styles'
|
import { createStyles, makeStyles } from '@material-ui/core/styles'
|
||||||
import { ReactElement, useContext, useEffect } from 'react'
|
import { ReactElement, useContext, useEffect } from 'react'
|
||||||
import { PlusSquare } from 'react-feather'
|
import { PlusSquare } from 'react-feather'
|
||||||
import { useHistory } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
import { SwarmButton } from '../../components/SwarmButton'
|
import { SwarmButton } from '../../components/SwarmButton'
|
||||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||||
import { Context as BeeContext } from '../../providers/Bee'
|
import { Context as BeeContext } from '../../providers/Bee'
|
||||||
@@ -29,7 +29,7 @@ const useStyles = makeStyles(() =>
|
|||||||
export default function Stamp(): ReactElement {
|
export default function Stamp(): ReactElement {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
const history = useHistory()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const { stamps, isLoading, error, start, stop } = useContext(StampsContext)
|
const { stamps, isLoading, error, start, stop } = useContext(StampsContext)
|
||||||
const { status } = useContext(BeeContext)
|
const { status } = useContext(BeeContext)
|
||||||
@@ -44,7 +44,7 @@ export default function Stamp(): ReactElement {
|
|||||||
if (!status.all) return <TroubleshootConnectionCard />
|
if (!status.all) return <TroubleshootConnectionCard />
|
||||||
|
|
||||||
function navigateToNewStamp() {
|
function navigateToNewStamp() {
|
||||||
history.push(ROUTES.STAMPS_NEW)
|
navigate(ROUTES.STAMPS_NEW)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type {
|
import type {
|
||||||
|
ChainState,
|
||||||
ChequebookAddressResponse,
|
ChequebookAddressResponse,
|
||||||
Health,
|
Health,
|
||||||
LastChequesResponse,
|
LastChequesResponse,
|
||||||
@@ -44,6 +45,7 @@ interface ContextInterface {
|
|||||||
peerBalances: Balance[] | null
|
peerBalances: Balance[] | null
|
||||||
peerCheques: LastChequesResponse | null
|
peerCheques: LastChequesResponse | null
|
||||||
settlements: Settlements | null
|
settlements: Settlements | null
|
||||||
|
chainState: ChainState | null
|
||||||
latestBeeRelease: LatestBeeRelease | null
|
latestBeeRelease: LatestBeeRelease | null
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
isRefreshing: boolean
|
isRefreshing: boolean
|
||||||
@@ -82,6 +84,7 @@ const initialValues: ContextInterface = {
|
|||||||
peerBalances: null,
|
peerBalances: null,
|
||||||
peerCheques: null,
|
peerCheques: null,
|
||||||
settlements: null,
|
settlements: null,
|
||||||
|
chainState: null,
|
||||||
latestBeeRelease: null,
|
latestBeeRelease: null,
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
isRefreshing: false,
|
isRefreshing: false,
|
||||||
@@ -144,6 +147,8 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
const [peerBalances, setPeerBalances] = useState<Balance[] | null>(null)
|
const [peerBalances, setPeerBalances] = useState<Balance[] | null>(null)
|
||||||
const [peerCheques, setPeerCheques] = useState<LastChequesResponse | null>(null)
|
const [peerCheques, setPeerCheques] = useState<LastChequesResponse | null>(null)
|
||||||
const [settlements, setSettlements] = useState<Settlements | null>(null)
|
const [settlements, setSettlements] = useState<Settlements | null>(null)
|
||||||
|
const [chainState, setChainState] = useState<ChainState | null>(null)
|
||||||
|
|
||||||
const { latestBeeRelease } = useLatestBeeRelease()
|
const { latestBeeRelease } = useLatestBeeRelease()
|
||||||
|
|
||||||
const [error, setError] = useState<Error | null>(initialValues.error)
|
const [error, setError] = useState<Error | null>(initialValues.error)
|
||||||
@@ -177,6 +182,7 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
setPeerBalances(null)
|
setPeerBalances(null)
|
||||||
setPeerCheques(null)
|
setPeerCheques(null)
|
||||||
setSettlements(null)
|
setSettlements(null)
|
||||||
|
setChainState(null)
|
||||||
|
|
||||||
refresh()
|
refresh()
|
||||||
}, [beeDebugApi]) // eslint-disable-line react-hooks/exhaustive-deps
|
}, [beeDebugApi]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
@@ -277,6 +283,12 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
.then(setPeerCheques)
|
.then(setPeerCheques)
|
||||||
.catch(() => setPeerCheques(null)),
|
.catch(() => setPeerCheques(null)),
|
||||||
|
|
||||||
|
// Chain state
|
||||||
|
beeDebugApi
|
||||||
|
.getChainState()
|
||||||
|
.then(setChainState)
|
||||||
|
.catch(() => setChainState(null)),
|
||||||
|
|
||||||
// Chequebook balance
|
// Chequebook balance
|
||||||
chequeBalanceWrapper()
|
chequeBalanceWrapper()
|
||||||
.then(setChequebookBalance)
|
.then(setChequebookBalance)
|
||||||
@@ -354,6 +366,7 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
peerBalances,
|
peerBalances,
|
||||||
peerCheques,
|
peerCheques,
|
||||||
settlements,
|
settlements,
|
||||||
|
chainState,
|
||||||
latestBeeRelease,
|
latestBeeRelease,
|
||||||
isLoading,
|
isLoading,
|
||||||
isRefreshing,
|
isRefreshing,
|
||||||
|
|||||||
+41
-6
@@ -1,17 +1,22 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
|
|
||||||
import { createContext, ReactChild, ReactElement, useState } from 'react'
|
import { createContext, ReactChild, ReactElement, useState, useEffect } from 'react'
|
||||||
import { SwarmFile } from '../utils/SwarmFile'
|
import { getMetadata } from '../utils/file'
|
||||||
|
import { resize } from '../utils/image'
|
||||||
|
import { PREVIEW_DIMENSIONS } from '../constants'
|
||||||
|
|
||||||
export type UploadOrigin = { origin: 'UPLOAD' | 'FEED'; uuid?: string }
|
export type UploadOrigin = { origin: 'UPLOAD' | 'FEED'; uuid?: string }
|
||||||
|
|
||||||
export const defaultUploadOrigin: UploadOrigin = { origin: 'UPLOAD' }
|
export const defaultUploadOrigin: UploadOrigin = { origin: 'UPLOAD' }
|
||||||
|
|
||||||
interface ContextInterface {
|
interface ContextInterface {
|
||||||
files: SwarmFile[]
|
files: FilePath[]
|
||||||
setFiles: (files: SwarmFile[]) => void
|
setFiles: (files: FilePath[]) => void
|
||||||
uploadOrigin: UploadOrigin
|
uploadOrigin: UploadOrigin
|
||||||
setUploadOrigin: (uploadOrigin: UploadOrigin) => void
|
setUploadOrigin: (uploadOrigin: UploadOrigin) => void
|
||||||
|
metadata?: Metadata
|
||||||
|
previewUri?: string
|
||||||
|
previewBlob?: Blob
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialValues: ContextInterface = {
|
const initialValues: ContextInterface = {
|
||||||
@@ -29,8 +34,38 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Provider({ children }: Props): ReactElement {
|
export function Provider({ children }: Props): ReactElement {
|
||||||
const [files, setFiles] = useState<SwarmFile[]>(initialValues.files)
|
const [files, setFiles] = useState<FilePath[]>(initialValues.files)
|
||||||
const [uploadOrigin, setUploadOrigin] = useState<UploadOrigin>(initialValues.uploadOrigin)
|
const [uploadOrigin, setUploadOrigin] = useState<UploadOrigin>(initialValues.uploadOrigin)
|
||||||
|
const [metadata, setMetadata] = useState<Metadata | undefined>(undefined)
|
||||||
|
const [previewUri, setPreviewUri] = useState<string | undefined>(undefined)
|
||||||
|
const [previewBlob, setPreviewBlob] = useState<Blob | undefined>(undefined)
|
||||||
|
|
||||||
return <Context.Provider value={{ files, setFiles, uploadOrigin, setUploadOrigin }}>{children}</Context.Provider>
|
useEffect(() => {
|
||||||
|
setMetadata(getMetadata(files))
|
||||||
|
|
||||||
|
if (previewUri) {
|
||||||
|
URL.revokeObjectURL(previewUri) // Clear the preview from memory
|
||||||
|
setPreviewUri(undefined)
|
||||||
|
setPreviewBlob(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.length !== 1 || !files[0].type.startsWith('image')) return
|
||||||
|
|
||||||
|
resize(files[0], PREVIEW_DIMENSIONS.maxWidth, PREVIEW_DIMENSIONS.maxHeight).then(blob => {
|
||||||
|
setPreviewUri(URL.createObjectURL(blob)) // NOTE: Until it is cleared with URL.revokeObjectURL, the file stays allocated in memory
|
||||||
|
setPreviewBlob(blob)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (previewUri) {
|
||||||
|
URL.revokeObjectURL(previewUri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [files]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Context.Provider value={{ files, setFiles, uploadOrigin, setUploadOrigin, metadata, previewUri, previewBlob }}>
|
||||||
|
{children}
|
||||||
|
</Context.Provider>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-15
@@ -44,35 +44,38 @@ export function Provider({
|
|||||||
const [beeDebugApi, setBeeDebugApi] = useState<BeeDebug | null>(null)
|
const [beeDebugApi, setBeeDebugApi] = useState<BeeDebug | null>(null)
|
||||||
const [lockedApiSettings] = useState<boolean>(Boolean(extLockedApiSettings))
|
const [lockedApiSettings] = useState<boolean>(Boolean(extLockedApiSettings))
|
||||||
|
|
||||||
|
const url = beeApiUrl || apiUrl
|
||||||
|
const debugUrl = beeDebugApiUrl || apiDebugUrl
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
setBeeApi(new Bee(apiUrl))
|
setBeeApi(new Bee(url))
|
||||||
sessionStorage.setItem('api_host', apiUrl)
|
sessionStorage.setItem('api_host', url)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setBeeApi(null)
|
setBeeApi(null)
|
||||||
}
|
}
|
||||||
}, [apiUrl])
|
}, [url])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (beeApiUrl) setApiUrl(beeApiUrl)
|
|
||||||
}, [beeApiUrl])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (beeDebugApiUrl) setDebugApiUrl(beeDebugApiUrl)
|
|
||||||
}, [beeDebugApiUrl])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
setBeeDebugApi(new BeeDebug(apiDebugUrl))
|
setBeeDebugApi(new BeeDebug(debugUrl))
|
||||||
sessionStorage.setItem('debug_api_host', apiDebugUrl)
|
sessionStorage.setItem('debug_api_host', debugUrl)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setBeeDebugApi(null)
|
setBeeDebugApi(null)
|
||||||
}
|
}
|
||||||
}, [apiDebugUrl])
|
}, [debugUrl])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Context.Provider
|
<Context.Provider
|
||||||
value={{ apiUrl, apiDebugUrl, beeApi, beeDebugApi, setApiUrl, setDebugApiUrl, lockedApiSettings }}
|
value={{
|
||||||
|
apiUrl: url,
|
||||||
|
apiDebugUrl: debugUrl,
|
||||||
|
beeApi,
|
||||||
|
beeDebugApi,
|
||||||
|
setApiUrl,
|
||||||
|
setDebugApiUrl,
|
||||||
|
lockedApiSettings,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Context.Provider>
|
</Context.Provider>
|
||||||
|
|||||||
Vendored
+15
@@ -21,3 +21,18 @@ interface StatusEthereumConnectionHook extends StatusHookCommon {
|
|||||||
interface StatusTopologyHook extends StatusHookCommon {
|
interface StatusTopologyHook extends StatusHookCommon {
|
||||||
topology: Topology | null
|
topology: Topology | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SwarmMetadata {
|
||||||
|
size: number
|
||||||
|
name: string
|
||||||
|
type?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Metadata extends SwarmMetadata {
|
||||||
|
type: string
|
||||||
|
isWebsite: boolean
|
||||||
|
count?: number
|
||||||
|
hash?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FilePath = File & { path?: string; fullPath?: string }
|
||||||
|
|||||||
+17
-17
@@ -1,5 +1,5 @@
|
|||||||
import type { ReactElement } from 'react'
|
import type { ReactElement } from 'react'
|
||||||
import { Route, Switch } from 'react-router-dom'
|
import { Route, Routes } from 'react-router-dom'
|
||||||
import Accounting from './pages/accounting'
|
import Accounting from './pages/accounting'
|
||||||
import Feeds from './pages/feeds'
|
import Feeds from './pages/feeds'
|
||||||
import CreateNewFeed from './pages/feeds/CreateNewFeed'
|
import CreateNewFeed from './pages/feeds/CreateNewFeed'
|
||||||
@@ -34,22 +34,22 @@ export enum ROUTES {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const BaseRouter = (): ReactElement => (
|
const BaseRouter = (): ReactElement => (
|
||||||
<Switch>
|
<Routes>
|
||||||
<Route exact path={ROUTES.UPLOAD_IN_PROGRESS} component={Upload} />
|
<Route path={ROUTES.UPLOAD_IN_PROGRESS} element={<Upload />} />
|
||||||
<Route exact path={ROUTES.UPLOAD} component={UploadLander} />
|
<Route path={ROUTES.UPLOAD} element={<UploadLander />} />
|
||||||
<Route exact path={ROUTES.DOWNLOAD} component={Download} />
|
<Route path={ROUTES.DOWNLOAD} element={<Download />} />
|
||||||
<Route exact path={ROUTES.HASH} component={Share} />
|
<Route path={ROUTES.HASH} element={<Share />} />
|
||||||
<Route exact path={ROUTES.ACCOUNTING} component={Accounting} />
|
<Route path={ROUTES.ACCOUNTING} element={<Accounting />} />
|
||||||
<Route exact path={ROUTES.SETTINGS} component={Settings} />
|
<Route path={ROUTES.SETTINGS} element={<Settings />} />
|
||||||
<Route exact path={ROUTES.STAMPS} component={Stamps} />
|
<Route path={ROUTES.STAMPS} element={<Stamps />} />
|
||||||
<Route exact path={ROUTES.STAMPS_NEW} component={CreatePostageStampPage} />
|
<Route path={ROUTES.STAMPS_NEW} element={<CreatePostageStampPage />} />
|
||||||
<Route exact path={ROUTES.STATUS} component={Status} />
|
<Route path={ROUTES.STATUS} element={<Status />} />
|
||||||
<Route exact path={ROUTES.FEEDS} component={Feeds} />
|
<Route path={ROUTES.FEEDS} element={<Feeds />} />
|
||||||
<Route exact path={ROUTES.FEEDS_NEW} component={CreateNewFeed} />
|
<Route path={ROUTES.FEEDS_NEW} element={<CreateNewFeed />} />
|
||||||
<Route exact path={ROUTES.FEEDS_UPDATE} component={UpdateFeed} />
|
<Route path={ROUTES.FEEDS_UPDATE} element={<UpdateFeed />} />
|
||||||
<Route exact path={ROUTES.FEEDS_PAGE} component={FeedSubpage} />
|
<Route path={ROUTES.FEEDS_PAGE} element={<FeedSubpage />} />
|
||||||
<Route path={ROUTES.INFO} component={Info} />
|
<Route path={ROUTES.INFO} element={<Info />} />
|
||||||
</Switch>
|
</Routes>
|
||||||
)
|
)
|
||||||
|
|
||||||
export default BaseRouter
|
export default BaseRouter
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
export class SwarmFile {
|
|
||||||
public name: string
|
|
||||||
public path: string
|
|
||||||
public type: string
|
|
||||||
public size: number
|
|
||||||
public webkitRelativePath: string
|
|
||||||
public arrayBuffer: () => Promise<ArrayBuffer>
|
|
||||||
private data: Promise<ArrayBuffer>
|
|
||||||
|
|
||||||
constructor(file: File) {
|
|
||||||
const path = Reflect.get(file, 'path') || file.webkitRelativePath || file.name
|
|
||||||
this.path = path.startsWith('/') ? path.slice(1) : path
|
|
||||||
this.webkitRelativePath = this.path
|
|
||||||
this.name = file.name
|
|
||||||
this.type = file.type
|
|
||||||
this.size = file.size
|
|
||||||
this.data = file.arrayBuffer()
|
|
||||||
this.arrayBuffer = async () => {
|
|
||||||
const data = await this.data
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+58
-41
@@ -1,28 +1,32 @@
|
|||||||
import { FileData } from '@ethersphere/bee-js'
|
|
||||||
import { SwarmFile } from './SwarmFile'
|
|
||||||
|
|
||||||
const indexHtmls = ['index.html', 'index.htm']
|
const indexHtmls = ['index.html', 'index.htm']
|
||||||
|
|
||||||
export function detectIndexHtml(files: SwarmFile[]): string | false {
|
interface DetectedIndex {
|
||||||
if (!files.length) {
|
indexPath: string
|
||||||
|
commonPrefix?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function detectIndexHtml(files: FilePath[]): DetectedIndex | false {
|
||||||
|
const paths = files.map(getPath)
|
||||||
|
|
||||||
|
if (!paths.length) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const exactMatch = files.find(x => indexHtmls.includes(x.path))
|
const exactMatch = paths.find(x => indexHtmls.includes(x))
|
||||||
|
|
||||||
if (exactMatch) {
|
if (exactMatch) {
|
||||||
return exactMatch.name
|
return { indexPath: exactMatch }
|
||||||
}
|
}
|
||||||
|
|
||||||
const prefix = files[0].path.split('/')[0] + '/'
|
const prefix = paths[0].split('/')[0] + '/'
|
||||||
|
|
||||||
const allStartWithSamePrefix = files.every(x => x.path.startsWith(prefix))
|
const allStartWithSamePrefix = paths.every(x => x.startsWith(prefix))
|
||||||
|
|
||||||
if (allStartWithSamePrefix) {
|
if (allStartWithSamePrefix) {
|
||||||
const match = files.find(x => indexHtmls.map(y => prefix + y).includes(x.path))
|
const match = paths.find(x => indexHtmls.map(y => prefix + y).includes(x))
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
return match.name
|
return { indexPath: match, commonPrefix: prefix }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,37 +57,50 @@ export function getHumanReadableFileSize(bytes: number): string {
|
|||||||
return bytes + ' bytes'
|
return bytes + ' bytes'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertBeeFileToBrowserFile(file: FileData<ArrayBuffer>): Partial<File> {
|
export function getAssetNameFromFiles(files: FilePath[]): string {
|
||||||
|
if (files.length === 1) return files[0].name
|
||||||
|
|
||||||
|
if (files.length > 0) {
|
||||||
|
const prefix = getPath(files[0]).split('/')[0]
|
||||||
|
|
||||||
|
// Only if all files have a common prefix we can use it as a folder name
|
||||||
|
if (files.every(f => getPath(f).split('/')[0] === prefix)) return prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMetadata(files: FilePath[]): Metadata {
|
||||||
|
const size = files.reduce((total, item) => total + item.size, 0)
|
||||||
|
const isWebsite = Boolean(detectIndexHtml(files))
|
||||||
|
const name = getAssetNameFromFiles(files)
|
||||||
|
const type = files.length === 1 ? files[0].type : 'folder'
|
||||||
|
const count = files.length
|
||||||
|
|
||||||
|
return { size, name, type, isWebsite, count }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPath(file: FilePath): string {
|
||||||
|
return (file.path || file.webkitRelativePath || file.name).replace(/^\//g, '') // remove the starting slash
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility function that is needed to have correct directory structure as webkitRelativePath is read only
|
||||||
|
*/
|
||||||
|
export function packageFile(file: FilePath): FilePath {
|
||||||
|
const path = getPath(file)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
path: path,
|
||||||
|
fullPath: path,
|
||||||
|
webkitRelativePath: path,
|
||||||
|
lastModified: file.lastModified,
|
||||||
name: file.name,
|
name: file.name,
|
||||||
size: file.data.byteLength,
|
size: file.size,
|
||||||
type: file.contentType,
|
type: file.type,
|
||||||
arrayBuffer: () => new Promise(resolve => resolve(file.data)),
|
stream: file.stream,
|
||||||
|
slice: file.slice,
|
||||||
|
text: file.text,
|
||||||
|
arrayBuffer: async () => await file.arrayBuffer(), // This is needed for successful upload and can not simply be { arrayBuffer: file.arrayBuffer }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertManifestToFiles(files: Record<string, string>): SwarmFile[] {
|
|
||||||
return Object.entries(files).map(
|
|
||||||
x =>
|
|
||||||
({
|
|
||||||
name: x[0],
|
|
||||||
path: x[0],
|
|
||||||
type: 'n/a',
|
|
||||||
size: 0,
|
|
||||||
webkitRelativePath: x[0],
|
|
||||||
arrayBuffer: () => new Promise(resolve => resolve(new ArrayBuffer(0))),
|
|
||||||
} as SwarmFile),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAssetNameFromFiles(files: SwarmFile[]): string {
|
|
||||||
if (!files.length) {
|
|
||||||
return 'Unknown'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.length === 1) {
|
|
||||||
return files[0].name
|
|
||||||
}
|
|
||||||
|
|
||||||
return files[0].path.split('/')[0]
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
interface Dimensions {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dimensions of the image after resize
|
||||||
|
*
|
||||||
|
* @param imgWidth Current image width
|
||||||
|
* @param imgHeight Current image height
|
||||||
|
* @param maxWidth Desired max width
|
||||||
|
* @param maxHeight Desired max height
|
||||||
|
*
|
||||||
|
* @returns Downscaled dimensions of the image to fit in the bounding box
|
||||||
|
*/
|
||||||
|
export function getDimensions(imgWidth: number, imgHeight: number, maxWidth?: number, maxHeight?: number): Dimensions {
|
||||||
|
const ratioWidth = maxWidth ? imgWidth / maxWidth : 1
|
||||||
|
const ratioHeight = maxHeight ? imgHeight / maxHeight : 1
|
||||||
|
|
||||||
|
const ratio = Math.max(ratioWidth, ratioHeight)
|
||||||
|
|
||||||
|
// No need to resize
|
||||||
|
if (ratio <= 1) return { width: imgWidth, height: imgHeight }
|
||||||
|
|
||||||
|
return { width: imgWidth / ratio, height: imgHeight / ratio }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize image passed to fit in the bounding box defined with maxWidth and maxHeight.
|
||||||
|
* Note that one or both of the bounding box dimensions may be omitted
|
||||||
|
*
|
||||||
|
* @param file Image file to be resized
|
||||||
|
* @param maxWidth Maximal image width
|
||||||
|
* @param maxHeight Maximal image height
|
||||||
|
*
|
||||||
|
* @returns Promise that resolves into the resized image blob
|
||||||
|
*/
|
||||||
|
export function resize(file: File, maxWidth?: number, maxHeight?: number): Promise<Blob> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const allowedTypes = [
|
||||||
|
'image/bmp',
|
||||||
|
'image/gif',
|
||||||
|
'image/vnd.microsoft.icon',
|
||||||
|
'image/jpeg',
|
||||||
|
'image/png',
|
||||||
|
'image/svg+xml',
|
||||||
|
'image/tiff',
|
||||||
|
'image/webp',
|
||||||
|
]
|
||||||
|
|
||||||
|
if (!file.size || !file.type || !allowedTypes.includes(file.type)) return reject('File not supported!')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
reader.onload = event => {
|
||||||
|
const src = event?.target?.result
|
||||||
|
|
||||||
|
if (!src || typeof src !== 'string') throw new Error('Failed to load the image source')
|
||||||
|
|
||||||
|
const img = new Image()
|
||||||
|
img.src = src
|
||||||
|
img.onload = () => {
|
||||||
|
const dimensions = getDimensions(img.width, img.height, maxWidth, maxHeight)
|
||||||
|
const elem = document.createElement('canvas')
|
||||||
|
elem.width = dimensions.width
|
||||||
|
elem.height = dimensions.height
|
||||||
|
const ctx = elem.getContext('2d')
|
||||||
|
|
||||||
|
if (!ctx) throw new Error('Failed to create canvas context')
|
||||||
|
|
||||||
|
ctx.drawImage(img, 0, 0, elem.width, elem.height)
|
||||||
|
ctx.canvas.toBlob(
|
||||||
|
blob => {
|
||||||
|
if (!blob) throw new Error('Failed to extract the blob from canvas')
|
||||||
|
|
||||||
|
resolve(blob)
|
||||||
|
},
|
||||||
|
'image/jpeg',
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.onerror = error => reject(error)
|
||||||
|
} catch (error) {
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
+13
-2
@@ -1,3 +1,4 @@
|
|||||||
|
import { NumberString } from '@ethersphere/bee-js'
|
||||||
import { BigNumber } from 'bignumber.js'
|
import { BigNumber } from 'bignumber.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,6 +187,16 @@ export function convertAmountToSeconds(amount: number): number {
|
|||||||
return amount / 10 / 1
|
return amount / 10 / 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calculateStampPrice(depth: number, amount: number): number {
|
export function calculateStampPrice(depth: number, amount: number, currentPrice: NumberString): number {
|
||||||
return (amount * 2 ** (depth - 16) * 2) / 1e16
|
const price = parseInt(currentPrice, 10)
|
||||||
|
|
||||||
|
return (amount * 2 ** (depth - 16) * price) / 1e16
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shortenText(text: string, length = 20, separator = '[…]'): string {
|
||||||
|
if (text.length <= length * 2 + separator.length) {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${text.slice(0, length)}${separator}${text.slice(-length)}`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user