diff --git a/package-lock.json b/package-lock.json index 86ecc0b..7685c5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.2.0", "license": "BSD-3-Clause", "dependencies": { - "@ethersphere/bee-js": "^0.9.0", + "@ethersphere/bee-js": "^0.10.0", "@material-ui/core": "^4.11.4", "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.57", @@ -31,6 +31,7 @@ "react-identicons": "^1.2.5", "react-router-dom": "^5.2.0", "react-syntax-highlighter": "^15.4.3", + "semver": "^7.3.2", "serve-handler": "^6.1.3" }, "bin": { @@ -47,6 +48,7 @@ "@types/react-copy-to-clipboard": "^5.0.0", "@types/react-dom": "^17.0.3", "@types/react-syntax-highlighter": "^13.5.0", + "@types/semver": "^7.3.6", "eslint": "^7.24.0", "eslint-config-prettier": "^8.2.0", "eslint-plugin-jest": "^24.3.5", @@ -56,6 +58,11 @@ "react-scripts": "4.0.3", "typescript": "^4.2.4", "web-vitals": "^1.1.1" + }, + "engines": { + "bee": ">=0.6.0", + "node": ">=12.0.0", + "npm": ">=6.0.0" } }, "node_modules/@babel/code-frame": { @@ -1453,9 +1460,9 @@ } }, "node_modules/@ethersphere/bee-js": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-0.9.0.tgz", - "integrity": "sha512-C8lam7l+M5lLBOBKXyzIeSI2eE6R3jjBmP9bOJPZF8I42U5myNKp3t7QkIrdXZetrkAptVDISAZR0x0CZS2crQ==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-0.10.0.tgz", + "integrity": "sha512-j5HcrtN+LMmaqDNqdI5jUmU4YM039rV/vvOM2xsUX3+sFZBP00KVrpUsEMBzqQspU6U7//0y3BKx634V1hvpCA==", "dependencies": { "axios": "^0.21.1", "elliptic": "^6.5.4", @@ -1466,7 +1473,7 @@ "ws": "^7.4.4" }, "engines": { - "bee": "0.6.0-67cc8f24", + "bee": "0.6.2-984dca0a", "node": ">=12.0.0", "npm": ">=6.0.0" } @@ -2803,6 +2810,12 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" }, + "node_modules/@types/semver": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.6.tgz", + "integrity": "sha512-0caWDWmpCp0uifxFh+FaqK3CuZ2SkRR/ZRxAV5+zNdC3QVUi6wyOJnefhPvtNt8NQWXB5OA93BUvZsXpWat2Xw==", + "dev": true + }, "node_modules/@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", @@ -17283,7 +17296,6 @@ "version": "7.3.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true, "bin": { "semver": "bin/semver.js" }, @@ -22892,9 +22904,9 @@ } }, "@ethersphere/bee-js": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-0.9.0.tgz", - "integrity": "sha512-C8lam7l+M5lLBOBKXyzIeSI2eE6R3jjBmP9bOJPZF8I42U5myNKp3t7QkIrdXZetrkAptVDISAZR0x0CZS2crQ==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-0.10.0.tgz", + "integrity": "sha512-j5HcrtN+LMmaqDNqdI5jUmU4YM039rV/vvOM2xsUX3+sFZBP00KVrpUsEMBzqQspU6U7//0y3BKx634V1hvpCA==", "requires": { "axios": "^0.21.1", "elliptic": "^6.5.4", @@ -24029,6 +24041,12 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" }, + "@types/semver": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.6.tgz", + "integrity": "sha512-0caWDWmpCp0uifxFh+FaqK3CuZ2SkRR/ZRxAV5+zNdC3QVUi6wyOJnefhPvtNt8NQWXB5OA93BUvZsXpWat2Xw==", + "dev": true + }, "@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", @@ -36310,8 +36328,7 @@ "semver": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" }, "send": { "version": "0.17.1", diff --git a/package.json b/package.json index a8aa331..e1d9e46 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "url": "https://github.com/ethersphere/bee-dashboard.git" }, "dependencies": { - "@ethersphere/bee-js": "^0.9.0", + "@ethersphere/bee-js": "^0.10.0", "@material-ui/core": "^4.11.4", "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.57", @@ -46,6 +46,7 @@ "react-identicons": "^1.2.5", "react-router-dom": "^5.2.0", "react-syntax-highlighter": "^15.4.3", + "semver": "^7.3.2", "serve-handler": "^6.1.3" }, "devDependencies": { @@ -59,6 +60,7 @@ "@types/react-copy-to-clipboard": "^5.0.0", "@types/react-dom": "^17.0.3", "@types/react-syntax-highlighter": "^13.5.0", + "@types/semver": "^7.3.6", "eslint": "^7.24.0", "eslint-config-prettier": "^8.2.0", "eslint-plugin-jest": "^24.3.5", @@ -99,5 +101,10 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=6.0.0", + "bee": ">=0.6.0" } } diff --git a/src/components/AlertVersion.tsx b/src/components/AlertVersion.tsx new file mode 100644 index 0000000..b381534 --- /dev/null +++ b/src/components/AlertVersion.tsx @@ -0,0 +1,54 @@ +import { ReactElement, useState } from 'react' +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles' +import { Alert, AlertTitle } from '@material-ui/lab' +import Collapse from '@material-ui/core/Collapse' +import IconButton from '@material-ui/core/IconButton' +import CloseIcon from '@material-ui/icons/Close' +import { useStatusNodeVersion } from '../hooks/status' +import { SUPPORTED_BEE_VERSION_EXACT } from '@ethersphere/bee-js' + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + marginBottom: theme.spacing(2), + }, + }), +) + +export default function VersionAlert(): ReactElement | null { + const classes = useStyles() + const { isLoading, userVersion } = useStatusNodeVersion() + const [open, setOpen] = useState(true) + + const isExactlySupportedBeeVersion = SUPPORTED_BEE_VERSION_EXACT === userVersion + + if (isLoading) return null + + return ( + +
+ { + setOpen(false) + }} + > + + + } + > + Warning + Your Bee node version ({userVersion}) does not exactly match the Bee version we tested the Bee + Dashboard against ({SUPPORTED_BEE_VERSION_EXACT}). Please note that some functionality may not + work properly. + +
+
+ ) +} diff --git a/src/components/CashoutModal.tsx b/src/components/CashoutModal.tsx index 1e6d160..dc40b16 100644 --- a/src/components/CashoutModal.tsx +++ b/src/components/CashoutModal.tsx @@ -40,7 +40,7 @@ export default function DepositModal({ peerId, uncashedAmount }: Props): ReactEl enqueueSnackbar( Successfully cashed out cheque. Transaction - + , { variant: 'success' }, ) diff --git a/src/components/WDModal.tsx b/src/components/WDModal.tsx index e680534..0b8709e 100644 --- a/src/components/WDModal.tsx +++ b/src/components/WDModal.tsx @@ -18,7 +18,7 @@ interface Props { label: string max?: BigNumber min?: BigNumber - action: (amount: bigint) => Promise<{ transactionHash: string }> + action: (amount: bigint) => Promise } export default function WithdrawModal({ @@ -48,7 +48,7 @@ export default function WithdrawModal({ if (amountToken === null) return try { - const { transactionHash } = await action(amountToken.toBigInt as bigint) + const transactionHash = await action(amountToken.toBigInt as bigint) setOpen(false) enqueueSnackbar(`${successMessage} Transaction ${transactionHash}`, { variant: 'success' }) } catch (e) { diff --git a/src/hooks/status.ts b/src/hooks/status.ts index 2b66085..fa0588e 100644 --- a/src/hooks/status.ts +++ b/src/hooks/status.ts @@ -9,6 +9,8 @@ import { useDebugApiHealth, useLatestBeeRelease, } from './apiHooks' +import semver from 'semver' +import { engines } from '../../package.json' export interface StatusChequebookHook extends StatusHookCommon { chequebookBalance: ChequebookBalance | null @@ -19,12 +21,29 @@ export const useStatusNodeVersion = (): StatusNodeVersionHook => { const { latestBeeRelease, isLoadingLatestBeeRelease } = useLatestBeeRelease() const { nodeHealth, isLoadingNodeHealth } = useDebugApiHealth() + const latestVersion = semver.coerce(latestBeeRelease?.name)?.version + const latestUserVersion = semver.coerce(nodeHealth?.version)?.version + + const isLatestBeeVersion = Boolean( + latestVersion && + latestUserVersion && + semver.satisfies(latestVersion, latestUserVersion, { + includePrerelease: true, + }), + ) + return { isLoading: isLoadingNodeHealth || isLoadingLatestBeeRelease, - isOk: Boolean(latestBeeRelease && latestBeeRelease.name === `v${nodeHealth?.version?.split('-')[0]}`), - userVersion: nodeHealth?.version?.split('-')[0] || '-', - latestVersion: latestBeeRelease?.name.substring(1) || '-', + isOk: Boolean( + nodeHealth && + semver.satisfies(nodeHealth.version, engines.bee, { + includePrerelease: true, + }), + ), + userVersion: nodeHealth?.version, + latestVersion, latestUrl: latestBeeRelease?.html_url || 'https://github.com/ethersphere/bee/releases/latest', + isLatestBeeVersion, } } diff --git a/src/layout/Dashboard.tsx b/src/layout/Dashboard.tsx index 192c6c2..62d2041 100644 --- a/src/layout/Dashboard.tsx +++ b/src/layout/Dashboard.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, ReactElement } from 'react' import ErrorBoundary from '../components/ErrorBoundary' +import AlertVersion from '../components/AlertVersion' import { createStyles, Theme, makeStyles } from '@material-ui/core/styles' @@ -11,7 +12,6 @@ import { RouteComponentProps } from 'react-router' const useStyles = makeStyles((theme: Theme) => createStyles({ - toolbar: theme.mixins.toolbar, content: { marginLeft: '240px', flexGrow: 1, @@ -19,20 +19,6 @@ const useStyles = makeStyles((theme: Theme) => padding: theme.spacing(3), paddingBottom: '65px', }, - footer: { - marginLeft: '240px', - backgroundColor: theme.palette.background.default, - position: 'fixed', - bottom: 0, - flexGrow: 1, - width: '-webkit-fill-available', - padding: theme.spacing(2), - textAlign: 'center', - }, - logo: { - height: '20px', - marginRight: '7px', - }, }), ) @@ -73,7 +59,10 @@ const Dashboard = (props: Props): ReactElement => { -
{props.children}
+
+ + {props.children} +
) diff --git a/src/pages/status/StatusCard.tsx b/src/pages/status/StatusCard.tsx index c362e34..c9a8e1f 100644 --- a/src/pages/status/StatusCard.tsx +++ b/src/pages/status/StatusCard.tsx @@ -23,17 +23,19 @@ const useStyles = makeStyles(() => interface Props { nodeAddresses: NodeAddresses | null nodeTopology: Topology | null - userBeeVersion: string | null - latestBeeVersion: string | null + userBeeVersion?: string + isLatestBeeVersion: boolean isOk: boolean + latestUrl: string } function StatusCard({ userBeeVersion, nodeAddresses, nodeTopology, - latestBeeVersion, isOk, + isLatestBeeVersion, + latestUrl, }: Props): ReactElement | null { const classes = useStyles() @@ -72,7 +74,7 @@ function StatusCard({ Bee {' '} {userBeeVersion || '-'} - {userBeeVersion && latestBeeVersion && userBeeVersion === latestBeeVersion ? ( + {isLatestBeeVersion ? ( ) : ( - update + )} diff --git a/src/pages/status/index.tsx b/src/pages/status/index.tsx index ab2dab6..8a21eaf 100644 --- a/src/pages/status/index.tsx +++ b/src/pages/status/index.tsx @@ -49,9 +49,10 @@ export default function Status(): ReactElement {
c.isOk)} nodeTopology={topology.topology} + latestUrl={nodeVersion.latestUrl} nodeAddresses={ethereumConnection.nodeAddresses} /> {ethereumConnection.nodeAddresses && chequebook.chequebookAddress && ( diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index 88ef93f..acedcb3 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -11,9 +11,10 @@ interface StatusHookCommon { } interface StatusNodeVersionHook extends StatusHookCommon { - userVersion: string - latestVersion: string + userVersion?: string + latestVersion?: string latestUrl: string + isLatestBeeVersion: boolean } interface StatusEthereumConnectionHook extends StatusHookCommon { nodeAddresses: NodeAddresses | null diff --git a/src/services/bee.tsx b/src/services/bee.tsx index d319a9e..137be2e 100644 --- a/src/services/bee.tsx +++ b/src/services/bee.tsx @@ -4,11 +4,9 @@ import { BalanceResponse, Bee, BeeDebug, - CashoutResponse, ChequebookAddressResponse, ChequebookBalanceResponse, Data, - DepositTokensResponse, FileData, Health, LastCashoutActionResponse, @@ -21,7 +19,6 @@ import { PostageBatchOptions, Reference, Topology, - WithdrawTokensResponse, } from '@ethersphere/bee-js' import { apiHost, debugApiHost } from '../constants' @@ -88,7 +85,7 @@ export const beeDebugApi = { getLastCheques(): Promise { return beeJSDebugClient().getLastCheques() }, - peerCashout(peerId: string): Promise { + peerCashout(peerId: string): Promise { return beeJSDebugClient().cashoutLastCheque(peerId) }, getPeerLastCashout(peerId: string): Promise { @@ -97,10 +94,10 @@ export const beeDebugApi = { getPeerLastCheques(peerId: string): Promise { return beeJSDebugClient().getLastChequesForPeer(peerId) }, - withdraw(amount: bigint): Promise { + withdraw(amount: bigint): Promise { return beeJSDebugClient().withdrawTokens(amount) }, - deposit(amount: bigint): Promise { + deposit(amount: bigint): Promise { return beeJSDebugClient().depositTokens(amount) }, },