Compare commits

...

17 Commits

Author SHA1 Message Date
bee-worker f695ac3a1c chore(master): release 0.31.0 (#688) 2025-01-13 14:57:16 +01:00
Ferenc Sárai a6125b3d0b feat: remove experimental FDP menu item (#687)
Co-authored-by: Ferenc Sárai <ferenc.sarai@solarpunk.buzz>
2025-01-13 14:43:10 +01:00
bee-worker e01d9fe3d7 chore(master): release 0.30.0 (#675) 2024-11-25 12:35:42 +01:00
Cafe137 6294bb0a7b fix: allow changing api url (#676)
* fix: allow changing api url

* chore: bump ci

* fix: add missing hook dependency
2024-11-25 11:17:27 +01:00
Cafe137 fbb2ed8a57 feat: update map data (#684) 2024-11-25 10:47:30 +01:00
Cafe137 aef6c07371 chore: update bee-js (#683) 2024-11-25 09:51:14 +01:00
Cafe137 ed75198528 ci: remove update supported bee action (#682) 2024-11-25 09:27:24 +01:00
Cafe137 d0c94b7316 feat: add experimental fdp (#681)
* feat: add experimental fdp

* ci: update swarm-actions to v1

* fix: fix eslint violations

* refactor: decaf
2024-11-21 12:43:30 +01:00
Cafe137 63f338075b fix: explicitly define type 0 transaction (#674) 2024-10-03 15:27:17 +02:00
bee-worker 4cb0bcd3b9 chore(master): release 0.29.0 (#668) 2024-07-30 23:24:02 +02:00
Cafe137 01b1b39c42 feat: clarify labels and syncing (#670) 2024-07-17 19:30:21 +02:00
Cafe137 8558860f0a feat: polish app (#669) 2024-07-17 15:49:22 +02:00
tamas6 b4ebfc7c3f fix: clarify withdraw and deposit message (#654)
* fix: clear withdraw/deposit message

* fix: withdraw/deposit message shorten
2024-07-17 14:07:45 +02:00
bee-worker a47de8fcb5 chore(master): release 0.28.0 (#667) 2024-06-18 14:46:52 +02:00
Cafe137 e9ebe33d51 feat: upgrade bee-js to 7.0.3 (#666) 2024-06-18 14:46:04 +02:00
bee-worker 4c06ff5d8e chore(master): release 0.27.0 (#665) 2024-06-11 21:26:45 +02:00
rampall 999399fb08 feat: add redeem shortcut to sidebar 2024-06-11 21:22:11 +02:00
54 changed files with 54598 additions and 8613 deletions
-1
View File
@@ -9,7 +9,6 @@
"file-loader", "file-loader",
"ts-node", "ts-node",
"webpack-cli", "webpack-cli",
"assert",
"buffer", "buffer",
"crypto*", "crypto*",
"stream*", "stream*",
+2 -9
View File
@@ -56,13 +56,6 @@ jobs:
- name: Types check - name: Types check
run: npm run check:types run: npm run check:types
- name: Update supported Bee action
uses: ethersphere/update-supported-bee-action@v1
if: github.ref == 'refs/heads/master'
with:
updateEngine: true
token: ${{ secrets.GHA_PAT_BASIC }}
- name: Build - name: Build
run: npm run build run: npm run build
@@ -70,7 +63,7 @@ jobs:
run: npm run build:component run: npm run build:component
- name: Create preview - name: Create preview
uses: ethersphere/swarm-actions/pr-preview@v0 uses: ethersphere/swarm-actions/pr-preview@v1
continue-on-error: true continue-on-error: true
with: with:
bee-url: https://unlimited.gateway.ethswarm.org bee-url: https://unlimited.gateway.ethswarm.org
@@ -79,7 +72,7 @@ jobs:
headers: '${{ secrets.GATEWAY_AUTHORIZATION_HEADER }}' headers: '${{ secrets.GATEWAY_AUTHORIZATION_HEADER }}'
- name: Upload to testnet - name: Upload to testnet
uses: ethersphere/swarm-actions/upload-dir@v0 uses: ethersphere/swarm-actions/upload-dir@v1
continue-on-error: true continue-on-error: true
with: with:
index-document: index.html index-document: index.html
+48
View File
@@ -1,5 +1,53 @@
# Changelog # Changelog
## [0.31.0](https://github.com/ethersphere/bee-dashboard/compare/v0.30.0...v0.31.0) (2025-01-13)
### Features
* remove experimental FDP menu item ([#687](https://github.com/ethersphere/bee-dashboard/issues/687)) ([a6125b3](https://github.com/ethersphere/bee-dashboard/commit/a6125b3d0b0b680a9fa61a8edcd75b2ae6c153e0))
## [0.30.0](https://github.com/ethersphere/bee-dashboard/compare/v0.29.0...v0.30.0) (2024-11-25)
### Features
* add experimental fdp ([#681](https://github.com/ethersphere/bee-dashboard/issues/681)) ([d0c94b7](https://github.com/ethersphere/bee-dashboard/commit/d0c94b7316ea2b139bddc5481132ea7de7cb840d))
* update map data ([#684](https://github.com/ethersphere/bee-dashboard/issues/684)) ([fbb2ed8](https://github.com/ethersphere/bee-dashboard/commit/fbb2ed8a576f3519883e71382b7f4e8505fbe139))
### Bug Fixes
* allow changing api url ([#676](https://github.com/ethersphere/bee-dashboard/issues/676)) ([6294bb0](https://github.com/ethersphere/bee-dashboard/commit/6294bb0a7be6b9b82354c42da8c84e767fad899e))
* explicitly define type 0 transaction ([#674](https://github.com/ethersphere/bee-dashboard/issues/674)) ([63f3380](https://github.com/ethersphere/bee-dashboard/commit/63f338075b919cb70d79665c3d86537f2ac1d2e9))
## [0.29.0](https://github.com/ethersphere/bee-dashboard/compare/v0.28.0...v0.29.0) (2024-07-17)
### Features
* clarify labels and syncing ([#670](https://github.com/ethersphere/bee-dashboard/issues/670)) ([01b1b39](https://github.com/ethersphere/bee-dashboard/commit/01b1b39c42cc5b68a0132c3696c3c42a27ea2ee4))
* polish app ([#669](https://github.com/ethersphere/bee-dashboard/issues/669)) ([8558860](https://github.com/ethersphere/bee-dashboard/commit/8558860f0a3baa82c31c091a44c78bb8e97de70d))
### Bug Fixes
* clarify withdraw and deposit message ([#654](https://github.com/ethersphere/bee-dashboard/issues/654)) ([b4ebfc7](https://github.com/ethersphere/bee-dashboard/commit/b4ebfc7c3fd449807db47fa25763df464cc45618))
## [0.28.0](https://github.com/ethersphere/bee-dashboard/compare/v0.27.0...v0.28.0) (2024-06-18)
### Features
* upgrade bee-js to 7.0.3 ([#666](https://github.com/ethersphere/bee-dashboard/issues/666)) ([e9ebe33](https://github.com/ethersphere/bee-dashboard/commit/e9ebe33d51aa525921eacfad683577605e591531))
## [0.27.0](https://github.com/ethersphere/bee-dashboard/compare/v0.26.2...v0.27.0) (2024-06-11)
### Features
* add redeem shortcut to sidebar ([999399f](https://github.com/ethersphere/bee-dashboard/commit/999399fb08c1a47a671ba0ad50409624654a1082))
## [0.26.2](https://github.com/ethersphere/bee-dashboard/compare/v0.26.1...v0.26.2) (2024-06-05) ## [0.26.2](https://github.com/ethersphere/bee-dashboard/compare/v0.26.1...v0.26.2) (2024-06-05)
+2 -6
View File
@@ -13,12 +13,8 @@
**Warning: This project is in alpha state. There might (and most probably will) be changes in the future to its API and **Warning: This project is in alpha state. There might (and most probably will) be changes in the future to its API and
working. Also, no guarantees can be made about its stability, efficiency, and security at this stage.** working. Also, no guarantees can be made about its stability, efficiency, and security at this stage.**
This project is intended to be used with \*\*Bee version Stay up to date by joining the [official Discord](https://discord.gg/GU22h2utj6) and by keeping an eye on the
[releases tab](https://github.com/ethersphere/bee-dashboard/releases).
<!-- SUPPORTED_BEE_START -->1.12.0-88c1d236<!-- SUPPORTED_BEE_END -->**. Using it with older or newer Bee versions is
not recommended and may not work. Stay up to date by joining the [official Discord](https://discord.gg/GU22h2utj6) and
by keeping an eye on the [releases tab](https://github.com/ethersphere/bee-dashboard/releases).
![Status page](/ui_samples/info.png) ![Status page](/ui_samples/info.png)
+1552 -1333
View File
File diff suppressed because it is too large Load Diff
+13 -13
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ethersphere/bee-dashboard", "name": "@ethersphere/bee-dashboard",
"version": "0.26.2", "version": "0.31.0",
"description": "An app which helps users to setup their Bee node and do actions like cash out cheques", "description": "An app which helps users to setup their Bee node and do actions like cash out cheques",
"keywords": [ "keywords": [
"bee", "bee",
@@ -26,30 +26,30 @@
"url": "https://github.com/ethersphere/bee-dashboard.git" "url": "https://github.com/ethersphere/bee-dashboard.git"
}, },
"dependencies": { "dependencies": {
"@ethersphere/bee-js": "^7.0.0", "@ethersphere/bee-js": "^8.3.1",
"@ethersphere/swarm-cid": "^0.1.0", "@ethersphere/swarm-cid": "^0.1.0",
"@fairdatasociety/fdp-storage": "^0.19.0",
"@material-ui/core": "4.12.3", "@material-ui/core": "4.12.3",
"@material-ui/icons": "4.11.2", "@material-ui/icons": "4.11.2",
"@material-ui/lab": "4.0.0-alpha.57", "@material-ui/lab": "4.0.0-alpha.57",
"assert": "^2.0.0", "axios": "^0.28.1",
"axios": "0.24.0", "bignumber.js": "^9.1.2",
"bignumber.js": "9.0.1",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"crypto": "npm:crypto-browserify", "crypto": "npm:crypto-browserify",
"crypto-browserify": "^3.12.0", "crypto-browserify": "^3.12.0",
"dotted-map": "^2.2.3", "dotted-map": "^2.2.3",
"ethers": "^5.6.4", "ethers": "^5.7.2",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"formik": "2.2.9", "formik": "2.2.9",
"formik-material-ui": "3.0.1", "formik-material-ui": "3.0.1",
"jszip": "^3.7.1", "jszip": "^3.10.1",
"mantaray-js": "^1.0.3", "mantaray-js": "^1.0.3",
"material-ui-dropzone": "3.5.0", "material-ui-dropzone": "3.5.0",
"notistack": "1.0.10", "notistack": "^3.0.1",
"opener": "1.5.2", "opener": "1.5.2",
"qrcode.react": "1.0.1", "qrcode.react": "1.0.1",
"react": ">= 17.0.2", "react": ">= 17.0.2",
"react-copy-to-clipboard": "5.0.4", "react-copy-to-clipboard": "^5.1.0",
"react-dom": ">= 17.0.2", "react-dom": ">= 17.0.2",
"react-identicons": "1.2.5", "react-identicons": "1.2.5",
"react-router": "6.2.1", "react-router": "6.2.1",
@@ -77,15 +77,15 @@
"@types/jest": "27.0.2", "@types/jest": "27.0.2",
"@types/qrcode.react": "1.0.2", "@types/qrcode.react": "1.0.2",
"@types/react": "17.0.34", "@types/react": "17.0.34",
"@types/react-copy-to-clipboard": "5.0.2", "@types/react-copy-to-clipboard": "^5.0.2",
"@types/react-dom": "17.0.11", "@types/react-dom": "17.0.11",
"@types/react-router": "5.1.18", "@types/react-router": "5.1.18",
"@types/react-router-dom": "5.3.2", "@types/react-router-dom": "5.3.2",
"@types/react-syntax-highlighter": "13.5.2", "@types/react-syntax-highlighter": "13.5.2",
"@typescript-eslint/eslint-plugin": "5.28.0", "@typescript-eslint/eslint-plugin": "5.28.0",
"@typescript-eslint/parser": "5.28.0", "@typescript-eslint/parser": "5.28.0",
"babel-eslint": "10.1.0", "babel-eslint": "^10.1.0",
"babel-loader": "8.1.0", "babel-loader": "^8.1.0",
"babel-plugin-syntax-dynamic-import": "6.18.0", "babel-plugin-syntax-dynamic-import": "6.18.0",
"babel-plugin-tsconfig-paths": "1.0.2", "babel-plugin-tsconfig-paths": "1.0.2",
"base64-inline-loader": "^2.0.1", "base64-inline-loader": "^2.0.1",
@@ -113,7 +113,7 @@
"ts-node": "^10.8.1", "ts-node": "^10.8.1",
"typescript": "4.8.3", "typescript": "4.8.3",
"web-vitals": "2.1.2", "web-vitals": "2.1.2",
"webpack": "^5.73.0", "webpack": "^5.93.0",
"webpack-cli": "^4.10.0" "webpack-cli": "^4.10.0"
}, },
"peerDependencies": { "peerDependencies": {
+51739 -7115
View File
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
import { ChainState } from '@ethersphere/bee-js'
import { useContext, useEffect, useState } from 'react'
import { Context } from '../providers/Settings'
import ExpandableListItem from './ExpandableListItem'
export function ChainSync() {
const { beeApi } = useContext(Context)
const [chainState, setChainState] = useState<ChainState | null>(null)
useEffect(() => {
const interval = setInterval(() => {
if (!beeApi) {
return
}
beeApi.getChainState().then(setChainState).catch(console.error) // eslint-disable-line
}, 3_000)
return () => clearInterval(interval)
})
return (
<ExpandableListItem label="Chain state" value={chainState ? `${chainState.block} / ${chainState.chainTip}` : '-'} />
)
}
+9 -7
View File
@@ -1,9 +1,11 @@
import { Utils } from '@ethersphere/bee-js'
import { Typography } from '@material-ui/core/' import { Typography } from '@material-ui/core/'
import { ReactElement } from 'react' import { ReactElement } from 'react'
import Identicon from 'react-identicons' import Identicon from 'react-identicons'
import ClipboardCopy from './ClipboardCopy'
import QRCodeModal from './QRCodeModal'
import { BLOCKCHAIN_EXPLORER_URL } from '../constants' import { BLOCKCHAIN_EXPLORER_URL } from '../constants'
import ClipboardCopy from './ClipboardCopy'
import { Flex } from './Flex'
import QRCodeModal from './QRCodeModal'
interface Props { interface Props {
address: string | undefined address: string | undefined
@@ -16,10 +18,10 @@ export default function EthereumAddress(props: Props): ReactElement {
return ( return (
<Typography component="div" variant="subtitle1"> <Typography component="div" variant="subtitle1">
{props.address ? ( {props.address ? (
<div style={{ display: 'flex' }}> <Flex>
{props.hideBlockie ? null : ( {props.hideBlockie ? null : (
<div style={{ paddingTop: '5px', marginRight: '10px' }}> <div style={{ paddingTop: '5px', marginRight: '10px' }}>
<Identicon size={20} string={props.address} /> <Identicon size={20} string={Utils.capitalizeAddressERC55(props.address)} />
</div> </div>
)} )}
<div> <div>
@@ -43,9 +45,9 @@ export default function EthereumAddress(props: Props): ReactElement {
{props.address} {props.address}
</a> </a>
</div> </div>
<QRCodeModal value={props.address} label={'Ethereum Address'} /> <QRCodeModal value={Utils.capitalizeAddressERC55(props.address)} label={'Ethereum Address'} />
<ClipboardCopy value={props.address} /> <ClipboardCopy value={Utils.capitalizeAddressERC55(props.address)} />
</div> </Flex>
) : ( ) : (
'-' '-'
)} )}
+5 -4
View File
@@ -1,7 +1,8 @@
import { ReactElement, ReactNode, useState } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Collapse, ListItem, ListItemText, Typography } from '@material-ui/core' import { Collapse, ListItem, ListItemText, Typography } from '@material-ui/core'
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
import { ExpandLess, ExpandMore } from '@material-ui/icons' import { ExpandLess, ExpandMore } from '@material-ui/icons'
import { ReactElement, ReactNode, useState } from 'react'
import { Flex } from './Flex'
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
@@ -65,14 +66,14 @@ export default function ExpandableList({ children, label, level, defaultOpen, in
<div className={`${classes.root} ${rootLevelClass}`}> <div className={`${classes.root} ${rootLevelClass}`}>
<ListItem button onClick={handleClick} className={classes.header}> <ListItem button onClick={handleClick} className={classes.header}>
<ListItemText primary={<Typography variant={typographyVariant}>{label}</Typography>} /> <ListItemText primary={<Typography variant={typographyVariant}>{label}</Typography>} />
<div style={{ display: 'flex' }}> <Flex>
{!open && ( {!open && (
<Typography variant="body2" className={classes.infoText}> <Typography variant="body2" className={classes.infoText}>
{info} {info}
</Typography> </Typography>
)} )}
{open ? <ExpandLess /> : <ExpandMore />} {open ? <ExpandLess /> : <ExpandMore />}
</div> </Flex>
</ListItem> </ListItem>
<Collapse in={open} timeout="auto" unmountOnExit> <Collapse in={open} timeout="auto" unmountOnExit>
<div className={contentLevelClass}>{children}</div> <div className={contentLevelClass}>{children}</div>
+4 -8
View File
@@ -2,14 +2,14 @@ import { Box, Grid, IconButton, InputBase, ListItem, Typography } from '@materia
import Collapse from '@material-ui/core/Collapse' import Collapse from '@material-ui/core/Collapse'
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
import { ChangeEvent, ReactElement, useState } from 'react' import { ChangeEvent, ReactElement, useState } from 'react'
import type { RemixiconReactIconProps } from 'remixicon-react'
import Check from 'remixicon-react/CheckLineIcon' import Check from 'remixicon-react/CheckLineIcon'
import X from 'remixicon-react/CloseLineIcon'
import Edit from 'remixicon-react/PencilLineIcon' import Edit from 'remixicon-react/PencilLineIcon'
import Minus from 'remixicon-react/SubtractLineIcon' import Minus from 'remixicon-react/SubtractLineIcon'
import X from 'remixicon-react/CloseLineIcon'
import ExpandableListItemActions from './ExpandableListItemActions' import ExpandableListItemActions from './ExpandableListItemActions'
import ExpandableListItemNote from './ExpandableListItemNote' import ExpandableListItemNote from './ExpandableListItemNote'
import { SwarmButton } from './SwarmButton' import { SwarmButton } from './SwarmButton'
import type { RemixiconReactIconProps } from 'remixicon-react'
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
@@ -108,12 +108,8 @@ export default function ExpandableListItemKey({
<div> <div>
{!open && value} {!open && value}
{!expandedOnly && !locked && ( {!expandedOnly && !locked && (
<IconButton size="small" className={classes.copyValue}> <IconButton size="small" className={classes.copyValue} onClick={toggleOpen}>
{open ? ( {open ? <Minus strokeWidth={1} /> : <Edit strokeWidth={1} />}
<Minus onClick={toggleOpen} strokeWidth={1} />
) : (
<Edit onClick={toggleOpen} strokeWidth={1} />
)}
</IconButton> </IconButton>
)} )}
</div> </div>
+2 -4
View File
@@ -77,7 +77,6 @@ export default function ExpandableListItemKey({ label, value, expanded }: Props)
<Grid container direction="row" justifyContent="space-between" alignItems="center"> <Grid container direction="row" justifyContent="space-between" alignItems="center">
{label && <Typography variant="body1">{label}</Typography>} {label && <Typography variant="body1">{label}</Typography>}
<Typography variant="body2"> <Typography variant="body2">
<div>
{!open && ( {!open && (
<span className={classes.copyValue}> <span className={classes.copyValue}>
<Tooltip title={copied ? 'Copied' : 'Copy'} placement="top" arrow onClose={tooltipCloseHandler}> <Tooltip title={copied ? 'Copied' : 'Copy'} placement="top" arrow onClose={tooltipCloseHandler}>
@@ -87,10 +86,9 @@ export default function ExpandableListItemKey({ label, value, expanded }: Props)
</Tooltip> </Tooltip>
</span> </span>
)} )}
<IconButton size="small" className={classes.copyValue}> <IconButton size="small" className={classes.copyValue} onClick={toggleOpen}>
{open ? <Minus onClick={toggleOpen} strokeWidth={1} /> : <Eye onClick={toggleOpen} strokeWidth={1} />} {open ? <Minus strokeWidth={1} /> : <Eye strokeWidth={1} />}
</IconButton> </IconButton>
</div>
</Typography> </Typography>
</Grid> </Grid>
<Collapse in={open} timeout="auto" unmountOnExit> <Collapse in={open} timeout="auto" unmountOnExit>
+3 -5
View File
@@ -82,7 +82,6 @@ export default function ExpandableListItemLink({
<Grid container direction="row" justifyContent="space-between" alignItems="center"> <Grid container direction="row" justifyContent="space-between" alignItems="center">
{label && <Typography variant="body1">{label}</Typography>} {label && <Typography variant="body1">{label}</Typography>}
<Typography variant="body2"> <Typography variant="body2">
<div>
{allowClipboard && ( {allowClipboard && (
<span className={classes.copyValue}> <span className={classes.copyValue}>
<Tooltip title={copied ? 'Copied' : 'Copy'} placement="top" arrow onClose={tooltipCloseHandler}> <Tooltip title={copied ? 'Copied' : 'Copy'} placement="top" arrow onClose={tooltipCloseHandler}>
@@ -93,11 +92,10 @@ export default function ExpandableListItemLink({
</span> </span>
)} )}
{!allowClipboard && <span onClick={onNavigation}>{displayValue}</span>} {!allowClipboard && <span onClick={onNavigation}>{displayValue}</span>}
<IconButton size="small" className={classes.openLinkIcon}> <IconButton size="small" className={classes.openLinkIcon} onClick={onNavigation}>
{navigationType === 'NEW_WINDOW' && <OpenInNewSharp onClick={onNavigation} strokeWidth={1} />} {navigationType === 'NEW_WINDOW' && <OpenInNewSharp strokeWidth={1} />}
{navigationType === 'HISTORY_PUSH' && <ArrowForward onClick={onNavigation} strokeWidth={1} />} {navigationType === 'HISTORY_PUSH' && <ArrowForward strokeWidth={1} />}
</IconButton> </IconButton>
</div>
</Typography> </Typography>
</Grid> </Grid>
</Grid> </Grid>
+9
View File
@@ -0,0 +1,9 @@
import { ReactNode } from 'react'
interface Props {
children: ReactNode
}
export function Flex({ children }: Props) {
return <div style={{ display: 'flex' }}>{children}</div>
}
+8 -2
View File
@@ -1,5 +1,5 @@
import { BeeModes } from '@ethersphere/bee-js' import { BeeModes } from '@ethersphere/bee-js'
import { Divider, Drawer, Grid, List, Link as MUILink } from '@material-ui/core' import { Box, Divider, Drawer, Grid, List, Link as MUILink, Typography } from '@material-ui/core'
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles' import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'
import { ReactElement, useContext } from 'react' import { ReactElement, useContext } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
@@ -133,10 +133,16 @@ export default function SideBar(): ReactElement {
<SideBarItem <SideBarItem
iconStart={<GithubIcon className={classes.icon} />} iconStart={<GithubIcon className={classes.icon} />}
iconEnd={<ExternalLinkIcon className={classes.icon} color="#595959" />} iconEnd={<ExternalLinkIcon className={classes.icon} color="#595959" />}
label={<span>Github repository</span>} label={<span>GitHub</span>}
/> />
</MUILink> </MUILink>
</List> </List>
<Divider className={classes.divider} />
<Box mt={4}>
<Link to={ROUTES.TOP_UP_GIFT_CODE}>
<Typography align="center">Redeem gift code</Typography>
</Link>
</Box>
</Grid> </Grid>
<Grid> <Grid>
<List> <List>
+1 -1
View File
@@ -13,7 +13,7 @@ export default function DepositModal(): ReactElement {
<WithdrawDepositModal <WithdrawDepositModal
successMessage="Successful deposit." successMessage="Successful deposit."
errorMessage="Error with depositing" errorMessage="Error with depositing"
dialogMessage="Specify the amount of xBZZ you would like to deposit to your node." dialogMessage="Amount of xBZZ to deposit to the checkbook, from your node."
label="Deposit" label="Deposit"
icon={<Download size="1rem" />} icon={<Download size="1rem" />}
min={new BigNumber(0)} min={new BigNumber(0)}
+1 -1
View File
@@ -13,7 +13,7 @@ export default function WithdrawModal(): ReactElement {
<WithdrawDepositModal <WithdrawDepositModal
successMessage="Successful withdrawal." successMessage="Successful withdrawal."
errorMessage="Error with withdrawing." errorMessage="Error with withdrawing."
dialogMessage="Specify the amount of xBZZ you would like to withdraw from your node." dialogMessage="Amount of xBZZ to withdraw from the checkbook to your node."
label="Withdraw" label="Withdraw"
icon={<Upload size="1rem" />} icon={<Upload size="1rem" />}
min={new BigNumber(0)} min={new BigNumber(0)}
+3 -2
View File
@@ -4,6 +4,7 @@ import { useSnackbar } from 'notistack'
import React, { ReactElement, useContext, useEffect } from 'react' import React, { ReactElement, useContext, useEffect } from 'react'
import CloseIcon from 'remixicon-react/CloseCircleLineIcon' import CloseIcon from 'remixicon-react/CloseCircleLineIcon'
import ErrorBoundary from '../components/ErrorBoundary' import ErrorBoundary from '../components/ErrorBoundary'
import { Flex } from '../components/Flex'
import SideBar from '../components/SideBar' import SideBar from '../components/SideBar'
import { BEE_DESKTOP_LATEST_RELEASE_PAGE } from '../constants' import { BEE_DESKTOP_LATEST_RELEASE_PAGE } from '../constants'
import { useBeeDesktop, useNewBeeDesktopVersion } from '../hooks/apiHooks' import { useBeeDesktop, useNewBeeDesktopVersion } from '../hooks/apiHooks'
@@ -81,13 +82,13 @@ const Dashboard = (props: Props): ReactElement => {
) )
return ( return (
<div style={{ display: 'flex' }}> <Flex>
<SideBar /> <SideBar />
<Container className={classes.content}> <Container className={classes.content}>
{' '} {' '}
<ErrorBoundary errorReporting={props.errorReporting}>{content}</ErrorBoundary> <ErrorBoundary errorReporting={props.errorReporting}>{content}</ErrorBoundary>
</Container> </Container>
</div> </Flex>
) )
} }
+8 -7
View File
@@ -81,17 +81,18 @@ export class Token {
return asString.slice(0, indexOfSignificantDigit + digits) return asString.slice(0, indexOfSignificantDigit + digits)
} }
minusBaseUnits(amount: string): Token { minusBaseUnits(amount: string | BigNumber | bigint): Token {
const baseUnits = makeBigNumber(amount)
return new Token( return new Token(
this.toBigNumber.minus(new BigNumber(amount).multipliedBy(new BigNumber(10).pow(this.decimals))), this.toBigNumber.minus(baseUnits.multipliedBy(new BigNumber(10).pow(this.decimals))),
this.decimals, this.decimals,
) )
} }
plusBaseUnits(amount: string): Token { plusBaseUnits(amount: string | BigNumber | bigint): Token {
return new Token( const baseUnits = makeBigNumber(amount)
this.toBigNumber.plus(new BigNumber(amount).multipliedBy(new BigNumber(10).pow(this.decimals))),
this.decimals, return new Token(this.toBigNumber.plus(baseUnits.multipliedBy(new BigNumber(10).pow(this.decimals))), this.decimals)
)
} }
} }
@@ -1,3 +1,4 @@
import { Utils } from '@ethersphere/bee-js'
import { Box } from '@material-ui/core' import { Box } from '@material-ui/core'
import { ReactElement, useContext } from 'react' import { ReactElement, useContext } from 'react'
import ExpandableList from '../../../components/ExpandableList' import ExpandableList from '../../../components/ExpandableList'
@@ -57,7 +58,10 @@ export function AccountChequebook(): ReactElement {
</ExpandableList> </ExpandableList>
)} )}
<ExpandableList label="Blockchain" defaultOpen> <ExpandableList label="Blockchain" defaultOpen>
<ExpandableListItemKey label="Ethereum address" value={nodeAddresses?.ethereum || ''} /> <ExpandableListItemKey
label="Ethereum address"
value={nodeAddresses?.ethereum ? Utils.capitalizeAddressERC55(nodeAddresses.ethereum) : ''}
/>
<ExpandableListItemKey <ExpandableListItemKey
label="Chequebook contract address" label="Chequebook contract address"
value={chequebookAddress?.chequebookAddress || ''} value={chequebookAddress?.chequebookAddress || ''}
+2 -1
View File
@@ -2,6 +2,7 @@ import { CircularProgress, Container, createStyles, makeStyles } from '@material
import { ReactElement, useContext, useEffect } from 'react' import { ReactElement, useContext, useEffect } from 'react'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
import PlusSquare from 'remixicon-react/AddBoxLineIcon' import PlusSquare from 'remixicon-react/AddBoxLineIcon'
import { ChainSync } from '../../../components/ChainSync'
import { Loading } from '../../../components/Loading' import { Loading } from '../../../components/Loading'
import { SwarmButton } from '../../../components/SwarmButton' import { SwarmButton } from '../../../components/SwarmButton'
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard' import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
@@ -57,7 +58,7 @@ export function AccountStamps(): ReactElement {
{error && ( {error && (
<Container style={{ textAlign: 'center', padding: '50px' }}> <Container style={{ textAlign: 'center', padding: '50px' }}>
<Loading /> <Loading />
{error.message} <ChainSync />
</Container> </Container>
)} )}
{!error && ( {!error && (
+8 -4
View File
@@ -1,4 +1,4 @@
import { BeeModes } from '@ethersphere/bee-js' import { BeeModes, Utils } from '@ethersphere/bee-js'
import { Box, Grid, Typography } from '@material-ui/core' import { Box, Grid, Typography } from '@material-ui/core'
import { ReactElement, useContext } from 'react' import { ReactElement, useContext } from 'react'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
@@ -26,7 +26,7 @@ export function AccountWallet(): ReactElement {
const navigate = useNavigate() const navigate = useNavigate()
function onCheckTransactions() { function onCheckTransactions() {
window.open(`https://blockscout.com/xdai/mainnet/address/${nodeAddresses?.ethereum}/transactions`, '_blank') window.open(`https://gnosisscan.io/address/${nodeAddresses?.ethereum}`, '_blank')
} }
function onInvite() { function onInvite() {
@@ -56,7 +56,11 @@ export function AccountWallet(): ReactElement {
{balance && nodeAddresses ? ( {balance && nodeAddresses ? (
<> <>
<Box mb={0.25}> <Box mb={0.25}>
<ExpandableListItemKey label="Node wallet address" value={nodeAddresses.ethereum} expanded /> <ExpandableListItemKey
label="Node wallet address"
value={Utils.capitalizeAddressERC55(nodeAddresses.ethereum)}
expanded
/>
</Box> </Box>
<Box mb={0.25}> <Box mb={0.25}>
<ExpandableListItem label="xDAI balance" value={`${balance.dai.toSignificantDigits(4)} xDAI`} /> <ExpandableListItem label="xDAI balance" value={`${balance.dai.toSignificantDigits(4)} xDAI`} />
@@ -72,7 +76,7 @@ export function AccountWallet(): ReactElement {
)} )}
<ExpandableListItemActions> <ExpandableListItemActions>
<SwarmButton onClick={onCheckTransactions} iconType={Link}> <SwarmButton onClick={onCheckTransactions} iconType={Link}>
Check transactions on Blockscout Check transactions
</SwarmButton> </SwarmButton>
{isDesktop && ( {isDesktop && (
<SwarmButton onClick={onInvite} iconType={Gift}> <SwarmButton onClick={onInvite} iconType={Gift}>
+106
View File
@@ -0,0 +1,106 @@
import { FdpStorage } from '@fairdatasociety/fdp-storage'
import { Checkbox, InputBase, Typography } from '@material-ui/core'
import { useSnackbar } from 'notistack'
import { useEffect, useState } from 'react'
import RegisterIcon from 'remixicon-react/AddBoxLineIcon'
import LoginIcon from 'remixicon-react/LoginBoxLineIcon'
import { SwarmButton } from '../../components/SwarmButton'
import { Horizontal } from './Horizontal'
import { Vertical } from './Vertical'
interface Props {
fdp: FdpStorage
onSuccessfulLogin: () => void
}
export function FdpLogin({ fdp, onSuccessfulLogin }: Props) {
const [username, setUsername] = useState<string>('')
const [password, setPassword] = useState<string>('')
const [remember, setRemember] = useState<boolean>(false)
const [sepolia, setSepolia] = useState<string>('https://sepolia.drpc.org')
const { enqueueSnackbar } = useSnackbar()
const inputStyle = { background: 'white', padding: '2px 8px', width: '100%' }
useEffect(() => {
const storedSepolia = localStorage.getItem('sepolia')
if (storedSepolia) {
setSepolia(storedSepolia)
}
const fdpCredentials = localStorage.getItem('fdpCredentials')
if (fdpCredentials) {
const { username, password } = JSON.parse(fdpCredentials)
setUsername(username)
setPassword(password)
setRemember(true)
}
}, [])
async function onLogin() {
localStorage.setItem('sepolia', sepolia)
if (remember) {
localStorage.setItem('fdpCredentials', JSON.stringify({ username, password }))
} else {
localStorage.removeItem('fdpCredentials')
}
enqueueSnackbar('Logging in...', { variant: 'info' })
try {
await fdp.account.login(username, password)
enqueueSnackbar('Logged in successfully', { variant: 'success' })
onSuccessfulLogin()
} catch {
enqueueSnackbar('Login failed', { variant: 'error' })
} finally {
setUsername('')
setPassword('')
setRemember(false)
}
}
function onRegister() {
window.open('https://create.fairdatasociety.org/', '_blank')
}
return (
<div
style={{
maxWidth: '500px',
margin: 'auto',
}}
>
<Vertical gap={16} full>
<Vertical gap={8} left full>
<Typography variant="body2">Sepolia JSON RPC</Typography>
<InputBase value={sepolia} onChange={e => setSepolia(e.target.value)} style={inputStyle} />
</Vertical>
<Vertical gap={8} left full>
<Typography variant="body2">Username</Typography>
<InputBase value={username} onChange={e => setUsername(e.target.value)} style={inputStyle} />
</Vertical>
<Vertical gap={8} left full>
<Typography variant="body2">Password</Typography>
<InputBase value={password} onChange={e => setPassword(e.target.value)} style={inputStyle} type="password" />
</Vertical>
<Vertical gap={8} left full>
<Horizontal>
<Checkbox checked={remember} onChange={e => setRemember(e.target.checked)} />
<Typography variant="body2">Remember me</Typography>
</Horizontal>
</Vertical>
<Vertical left full>
<Horizontal gap={4}>
<SwarmButton iconType={LoginIcon} onClick={onLogin}>
Login
</SwarmButton>
<SwarmButton iconType={RegisterIcon} onClick={onRegister}>
Registration
</SwarmButton>
</Horizontal>
</Vertical>
</Vertical>
</div>
)
}
+98
View File
@@ -0,0 +1,98 @@
import { FdpStorage } from '@fairdatasociety/fdp-storage'
import { useState } from 'react'
import { CafeReactFs } from '../../react-fs/CafeReactFs'
import { FsItem, FsItemType } from '../../react-fs/CafeReactType'
import { joinUrl } from '../../react-fs/Utility'
interface Props {
fdp: FdpStorage
name: string
}
export function FdpPod({ fdp, name }: Props) {
const [reloader, setReloader] = useState(0)
function reload() {
setReloader(reloader + 1)
}
return (
<CafeReactFs
rootAlias={`/${name}`}
backgroundColor="#ffffff"
reloader={reloader}
onDeleteFile={async (path: string) => {
await fdp.file.delete(name, path)
reload()
}}
onDeleteDirectory={async (path: string) => {
await fdp.directory.delete(name, path)
reload()
}}
onUpload={(path: string) => {
const input = document.createElement('input')
input.type = 'file'
input.multiple = true
input.click()
return new Promise<void>(resolve => {
input.onchange = async () => {
if (!input.files || !input.files.length) {
resolve()
return
}
for (const file of Array.from(input.files)) {
const data = await file.arrayBuffer()
await fdp.file.uploadData(name, joinUrl(path, file.name), new Uint8Array(data))
}
reload()
resolve()
}
})
}}
onCreateDirectory={async (path: string) => {
// eslint-disable-next-line no-alert
const newDirectoryName = prompt('Directory name')
if (!newDirectoryName) {
return
}
await fdp.directory.create(name, joinUrl(path, newDirectoryName))
reload()
}}
// eslint-disable-next-line require-await
onSync={async () => {
setReloader(reloader + 1)
}}
download={async (path: string) => {
const data = await fdp.file.downloadData(name, path)
const url = URL.createObjectURL(new Blob([data]))
const a = document.createElement('a')
a.href = url
a.download = path.split('/').pop() || 'Untitled'
a.click()
}}
list={async (path: string) => {
const fdpResponse = await fdp.directory.read(name, path)
const items: FsItem[] = []
for (const directory of fdpResponse.directories) {
items.push({
name: directory.name,
$type: FsItemType.DIRECTORY,
id: directory.name,
})
}
for (const file of fdpResponse.files) {
items.push({
name: file.name,
$type: FsItemType.FILE,
id: file.name,
})
}
return items
}}
/>
)
}
+30
View File
@@ -0,0 +1,30 @@
import { FdpStorage } from '@fairdatasociety/fdp-storage'
import { Pod } from '@fairdatasociety/fdp-storage/dist/pod/types'
import { CircularProgress, Typography } from '@material-ui/core'
import { FdpPod } from './FdpPod'
import { Vertical } from './Vertical'
interface Props {
fdp: FdpStorage
pods: Pod[]
loadingPods: boolean
}
export function FdpPods({ fdp, pods, loadingPods }: Props) {
if (loadingPods) {
return (
<Vertical gap={32} full>
<CircularProgress />
<Typography>Loading your pods...</Typography>
</Vertical>
)
}
return (
<Vertical gap={16} full left>
{pods.map(pod => (
<FdpPod key={pod.index} fdp={fdp} name={pod.name} />
))}
</Vertical>
)
}
+22
View File
@@ -0,0 +1,22 @@
interface Props {
children: React.ReactNode
p?: string
gap?: number
between?: boolean
background?: string
}
export function Horizontal({ children, p = '0', gap = 8, between, background }: Props) {
const style = {
display: 'flex',
flexDirection: 'row' as 'row', //eslint-disable-line
alignItems: 'center',
justifyContent: between ? 'space-between' : 'flex-start',
gap: `${gap}px`,
padding: p,
background,
width: between ? '100%' : 'auto',
}
return <div style={style}>{children}</div>
}
+20
View File
@@ -0,0 +1,20 @@
interface Props {
children: React.ReactNode
p?: number
gap?: number
left?: boolean
full?: boolean
}
export function Vertical({ children, p = 0, gap = 0, left = false, full = false }: Props) {
const style = {
display: 'flex',
flexDirection: 'column' as 'column', //eslint-disable-line
alignItems: left ? 'flex-start' : 'center',
gap: `${gap}px`,
width: full ? '100%' : 'auto',
padding: `${p}px`,
}
return <div style={style}>{children}</div>
}
+163
View File
@@ -0,0 +1,163 @@
import { Bee } from '@ethersphere/bee-js'
import { FdpStorage } from '@fairdatasociety/fdp-storage'
import { Pod } from '@fairdatasociety/fdp-storage/dist/pod/types'
import { CircularProgress, Typography } from '@material-ui/core'
import { useSnackbar } from 'notistack'
import { ReactElement, useEffect, useState } from 'react'
import ImportIcon from 'remixicon-react/AddBoxLineIcon'
import PlusCircle from 'remixicon-react/AddCircleLineIcon'
import { SwarmButton } from '../../components/SwarmButton'
import { joinUrl } from '../../react-fs/Utility'
import { ManifestJs } from '../../utils/manifest'
import { FdpLogin } from './FdpLogin'
import { FdpPods } from './FdpPods'
import { Horizontal } from './Horizontal'
import { Vertical } from './Vertical'
async function makeFdp(): Promise<FdpStorage | null> {
const bee = new Bee('http://localhost:1633')
const sepolia = localStorage.getItem('sepolia') ?? 'https://sepolia.drpc.org'
const postageBatches = await bee.getAllPostageBatch()
const usableBatches = postageBatches.filter(batch => batch.usable)
const highestCapacityBatch = usableBatches.length ? usableBatches.reduce((a, b) => (a.depth > b.depth ? a : b)) : null
if (!highestCapacityBatch) {
return null
}
return new FdpStorage('http://localhost:1633', highestCapacityBatch.batchID, {
ensOptions: {
rpcUrl: sepolia,
contractAddresses: {
ensRegistry: '0x42a96D45d787685ac4b36292d218B106Fb39be7F',
fdsRegistrar: '0xFBF00389140C00384d88d458239833E3231a7414',
nameResolver: '0xE20ECe6Ea93c4edE41e4d3B973f6679F1E89986A',
publicResolver: '0xC904989B579c2B216A75723688C784038AA99B56',
reverseResolver: '0xbDC8D98d3cbFd68EA9c165E1f15Df6e77A2ae0C5',
},
gasEstimation: 1,
performChecks: true,
},
providerOptions: {
url: sepolia,
},
ensDomain: 'fds',
})
}
export default function FDP(): ReactElement {
const [fdp, setFdp] = useState<FdpStorage | null>(null)
const [pods, setPods] = useState<Pod[]>([])
const [loggedIn, setLoggedIn] = useState<boolean>(false)
const [loadingPods, setLoadingPods] = useState<boolean>(false)
const [creatingPod, setCreatingPod] = useState<boolean>(false)
const { enqueueSnackbar } = useSnackbar()
useEffect(() => {
makeFdp().then(fdp => {
if (!fdp) {
enqueueSnackbar('FDP could not be initialized. Do you have a postage batch?', { variant: 'error' })
}
setFdp(fdp)
})
}, [enqueueSnackbar])
useEffect(() => {
if (fdp && loggedIn) {
setLoadingPods(true)
fdp.personalStorage.list().then(pods => {
setPods(pods.pods)
setLoadingPods(false)
})
}
}, [fdp, loggedIn])
function onSuccessfulLogin() {
setLoggedIn(true)
}
function onCreatePod() {
if (!fdp) {
return
}
if (loadingPods || creatingPod) {
enqueueSnackbar('Please wait until the pods are loaded', { variant: 'info' })
return
}
// eslint-disable-next-line no-alert
const name = prompt('Enter a name for the new pod')
if (name) {
setCreatingPod(true)
fdp.personalStorage.create(name).then(() => {
fdp.personalStorage.list().then(pods => {
setPods(pods.pods)
setCreatingPod(false)
})
})
}
}
async function onImportPod() {
if (!fdp) {
return
}
if (loadingPods || creatingPod) {
enqueueSnackbar('Please wait until the pods are loaded', { variant: 'info' })
return
}
// eslint-disable-next-line no-alert
const name = prompt('Enter a name for the new pod')
// eslint-disable-next-line no-alert
const importHash = prompt('Enter the Swarm reference')
if (!name || !importHash) {
return
}
setCreatingPod(true)
const bee = new Bee('http://localhost:1633')
const manifestJs = new ManifestJs(bee)
const entries = await manifestJs.getHashes(importHash)
await fdp.personalStorage.create(name)
for (const [path, hash] of Object.entries(entries)) {
await fdp.file.uploadData(name, joinUrl('/', path), await bee.downloadData(hash))
}
const pods = await fdp.personalStorage.list()
setPods(pods.pods)
setCreatingPod(false)
}
if (!fdp) {
return <CircularProgress />
}
return (
<Vertical gap={32} full left>
<Horizontal between>
<Typography variant="h1">Files</Typography>
{loggedIn && (
<Horizontal gap={4}>
<SwarmButton onClick={onCreatePod} iconType={PlusCircle}>
Create
</SwarmButton>
<SwarmButton onClick={onImportPod} iconType={ImportIcon}>
Import
</SwarmButton>
</Horizontal>
)}
</Horizontal>
{!loggedIn && <FdpLogin fdp={fdp} onSuccessfulLogin={onSuccessfulLogin} />}
{loggedIn && <FdpPods fdp={fdp} pods={pods} loadingPods={loadingPods || creatingPod} />}
{loggedIn && !loadingPods && !creatingPod && pods.length === 0 && (
<Typography>
<strong>You do not have any pods yet.</strong> Get started by clicking the Create or Import button on the top
right.
</Typography>
)}
</Vertical>
)
}
+1 -1
View File
@@ -84,7 +84,7 @@ export function Download(): ReactElement {
<> <>
{nodeInfo?.beeMode !== BeeModes.ULTRA_LIGHT && <FileNavigation active="DOWNLOAD" />} {nodeInfo?.beeMode !== BeeModes.ULTRA_LIGHT && <FileNavigation active="DOWNLOAD" />}
<ExpandableListItemInput <ExpandableListItemInput
label="Swarm Hash" label="Swarm Hash or ENS"
onConfirm={value => onSwarmIdentifier(value)} onConfirm={value => onSwarmIdentifier(value)}
onChange={validateChange} onChange={validateChange}
helperText={referenceError} helperText={referenceError}
+3 -3
View File
@@ -17,12 +17,12 @@ export function ChequebookInfoCard() {
<Card <Card
buttonProps={{ buttonProps={{
iconType: ExchangeFunds, iconType: ExchangeFunds,
children: 'View chequebook', children: 'Manage chequebook',
onClick: () => navigate(ROUTES.ACCOUNT_CHEQUEBOOK), onClick: () => navigate(ROUTES.ACCOUNT_CHEQUEBOOK),
}} }}
icon={<ExchangeFunds />} icon={<ExchangeFunds />}
title={`${chequebookBalance?.availableBalance.toSignificantDigits(4)} xBZZ`} title={`${chequebookBalance?.availableBalance.toSignificantDigits(4)} xBZZ`}
subtitle="Current chequebook balance." subtitle="Network transfer balance."
status="ok" status="ok"
/> />
) )
@@ -32,7 +32,7 @@ export function ChequebookInfoCard() {
<Card <Card
buttonProps={{ buttonProps={{
iconType: ExchangeFunds, iconType: ExchangeFunds,
children: 'View chequebook', children: 'Manage chequebook',
onClick: () => navigate(ROUTES.ACCOUNT_CHEQUEBOOK), onClick: () => navigate(ROUTES.ACCOUNT_CHEQUEBOOK),
}} }}
icon={<ExchangeFunds />} icon={<ExchangeFunds />}
+13 -3
View File
@@ -1,5 +1,6 @@
import { Button } from '@material-ui/core' import { Button } from '@material-ui/core'
import { ReactElement, useContext } from 'react' import { ReactElement, useContext } from 'react'
import { ChainSync } from '../../components/ChainSync'
import ExpandableListItem from '../../components/ExpandableListItem' import ExpandableListItem from '../../components/ExpandableListItem'
import Map from '../../components/Map' import Map from '../../components/Map'
import { BEE_DESKTOP_LATEST_RELEASE_PAGE } from '../../constants' import { BEE_DESKTOP_LATEST_RELEASE_PAGE } from '../../constants'
@@ -19,11 +20,17 @@ export default function Status(): ReactElement {
return ( return (
<div> <div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'stretch', alignContent: 'stretch' }}> <div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'stretch',
alignContent: 'stretch',
gap: '8px',
}}
>
<NodeInfoCard /> <NodeInfoCard />
<div style={{ width: '8px' }}></div>
<WalletInfoCard /> <WalletInfoCard />
<div style={{ width: '8px' }}></div>
<ChequebookInfoCard /> <ChequebookInfoCard />
</div> </div>
<div style={{ height: '16px' }} /> <div style={{ height: '16px' }} />
@@ -31,6 +38,9 @@ export default function Status(): ReactElement {
<div style={{ height: '2px' }} /> <div style={{ height: '2px' }} />
<ExpandableListItem label="Connected peers" value={topology?.connected ?? '-'} /> <ExpandableListItem label="Connected peers" value={topology?.connected ?? '-'} />
<ExpandableListItem label="Population" value={topology?.population ?? '-'} /> <ExpandableListItem label="Population" value={topology?.population ?? '-'} />
<ExpandableListItem label="Depth" value={topology?.depth ?? '-'} />
<ChainSync />
<div style={{ height: '16px' }} /> <div style={{ height: '16px' }} />
{isDesktop && ( {isDesktop && (
<ExpandableListItem <ExpandableListItem
+19 -14
View File
@@ -1,35 +1,37 @@
import { BeeModes } from '@ethersphere/bee-js' import { BeeModes } from '@ethersphere/bee-js'
import { Box, Grid, Typography } from '@material-ui/core' import { Box, Grid, Typography } from '@material-ui/core'
import { useSnackbar } from 'notistack' import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useEffect, useState } from 'react' import { ReactElement, useContext, useEffect } from 'react'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
import { ChainSync } from '../../components/ChainSync'
import { Waiting } from '../../components/Waiting' import { Waiting } from '../../components/Waiting'
import { Context } from '../../providers/Bee' import { Context } from '../../providers/Settings'
import { ROUTES } from '../../routes' import { ROUTES } from '../../routes'
const STARTED_UPGRADE_AT = 'started-upgrade-at'
export default function LightModeRestart(): ReactElement { export default function LightModeRestart(): ReactElement {
const [startedAt] = useState(Number.parseInt(localStorage.getItem(STARTED_UPGRADE_AT) ?? Date.now().toFixed()))
const { apiHealth, nodeInfo } = useContext(Context)
const navigate = useNavigate() const navigate = useNavigate()
const { enqueueSnackbar } = useSnackbar() const { enqueueSnackbar } = useSnackbar()
const { beeApi } = useContext(Context)
useEffect(() => { useEffect(() => {
localStorage.setItem(STARTED_UPGRADE_AT, startedAt.toFixed()) if (!beeApi) {
}, [startedAt])
useEffect(() => {
if (Date.now() - startedAt < 45_000) {
return return
} }
if (apiHealth && nodeInfo?.beeMode === BeeModes.LIGHT) { const interval = setInterval(() => {
beeApi
.getNodeInfo()
.then(nodeInfo => {
if (nodeInfo.beeMode === BeeModes.LIGHT) {
enqueueSnackbar('Upgraded to light node', { variant: 'success' }) enqueueSnackbar('Upgraded to light node', { variant: 'success' })
localStorage.removeItem(STARTED_UPGRADE_AT)
navigate(ROUTES.INFO) navigate(ROUTES.INFO)
} }
}, [startedAt, navigate, nodeInfo, apiHealth, enqueueSnackbar]) })
.catch(console.error) // eslint-disable-line
}, 3_000)
return () => clearInterval(interval)
}, [beeApi, enqueueSnackbar, navigate])
return ( return (
<Grid container direction="column" justifyContent="center" alignItems="center"> <Grid container direction="column" justifyContent="center" alignItems="center">
@@ -41,9 +43,12 @@ export default function LightModeRestart(): ReactElement {
<strong>Upgrading Bee</strong> <strong>Upgrading Bee</strong>
</Typography> </Typography>
</Box> </Box>
<Box mb={1}>
<Typography> <Typography>
You will be redirected automatically once your node is up and running. This may take up to 10 minutes. You will be redirected automatically once your node is up and running. This may take up to 10 minutes.
</Typography> </Typography>
</Box>
<ChainSync />
</Grid> </Grid>
) )
} }
@@ -72,7 +72,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
return `${secondsToTimeString( return `${secondsToTimeString(
convertAmountToSeconds(amount, pricePerBlock), convertAmountToSeconds(amount, pricePerBlock),
)} (with price of ${pricePerBlock.toFixed(0)} per block)` )} (with price of ${pricePerBlock.toFixed(0)} PLUR per block)`
} }
function getPrice(depth: number, amount: bigint): string { function getPrice(depth: number, amount: bigint): string {
@@ -67,7 +67,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
return `${secondsToTimeString( return `${secondsToTimeString(
convertAmountToSeconds(parseInt(amount, 10), pricePerBlock), convertAmountToSeconds(parseInt(amount, 10), pricePerBlock),
)} (with price of ${pricePerBlock.toFixed(0)} per block)` )} (with price of ${pricePerBlock.toFixed(0)} PLUR per block)`
} }
function getPrice(depth: number, amount: bigint): string { function getPrice(depth: number, amount: bigint): string {
@@ -196,7 +196,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
</Box> </Box>
<Box display="flex" justifyContent={'right'} mt={0.5}> <Box display="flex" justifyContent={'right'} mt={0.5}>
<Typography style={{ fontSize: '10px', color: 'rgba(0, 0, 0, 0.26)' }}> <Typography style={{ fontSize: '10px', color: 'rgba(0, 0, 0, 0.26)' }}>
Current price of 24000 per block Current price of 24000 PLUR per block
</Typography> </Typography>
</Box> </Box>
</Box> </Box>
+6 -7
View File
@@ -65,17 +65,15 @@ export function Provider({ children, ...propsSettings }: Props): ReactElement {
const propsProviderUrl = const propsProviderUrl =
localStorage.getItem(LocalStorageKeys.providerUrl) || propsSettings.defaultRpcUrl || DEFAULT_RPC_URL localStorage.getItem(LocalStorageKeys.providerUrl) || propsSettings.defaultRpcUrl || DEFAULT_RPC_URL
const [apiUrl, setApiUrl] = useState<string>(initialValues.apiUrl) const [apiUrl, setApiUrl] = useState<string>(
sessionStorage.getItem('api_host') ?? propsSettings.beeApiUrl ?? initialValues.apiUrl,
)
const [beeApi, setBeeApi] = useState<Bee | null>(null) const [beeApi, setBeeApi] = useState<Bee | null>(null)
const [desktopApiKey, setDesktopApiKey] = useState<string>(initialValues.desktopApiKey) const [desktopApiKey, setDesktopApiKey] = useState<string>(initialValues.desktopApiKey)
const [rpcProviderUrl, setRpcProviderUrl] = useState(propsProviderUrl) const [rpcProviderUrl, setRpcProviderUrl] = useState(propsProviderUrl)
const [rpcProvider, setRpcProvider] = useState(new providers.JsonRpcProvider(propsProviderUrl)) const [rpcProvider, setRpcProvider] = useState(new providers.JsonRpcProvider(propsProviderUrl))
const { config, isLoading, error } = useGetBeeConfig(desktopUrl) const { config, isLoading, error } = useGetBeeConfig(desktopUrl)
const url = makeHttpUrl(
config?.['api-addr'] ?? sessionStorage.getItem('api_host') ?? propsSettings.beeApiUrl ?? apiUrl,
)
useEffect(() => { useEffect(() => {
const urlSearchParams = new URLSearchParams(window.location.search) const urlSearchParams = new URLSearchParams(window.location.search)
const newApiKey = urlSearchParams.get('v') const newApiKey = urlSearchParams.get('v')
@@ -88,18 +86,19 @@ export function Provider({ children, ...propsSettings }: Props): ReactElement {
}, []) }, [])
useEffect(() => { useEffect(() => {
const url = makeHttpUrl(config?.['api-addr'] ?? apiUrl)
try { try {
setBeeApi(new Bee(url)) setBeeApi(new Bee(url))
sessionStorage.setItem('api_host', url) sessionStorage.setItem('api_host', url)
} catch (e) { } catch (e) {
setBeeApi(null) setBeeApi(null)
} }
}, [url]) }, [config, apiUrl])
return ( return (
<Context.Provider <Context.Provider
value={{ value={{
apiUrl: url, apiUrl,
beeApi, beeApi,
setApiUrl, setApiUrl,
lockedApiSettings: Boolean(propsSettings.lockedApiSettings), lockedApiSettings: Boolean(propsSettings.lockedApiSettings),
+1
View File
@@ -70,6 +70,7 @@ export function Provider({ children }: Props): ReactElement {
setStamps(stamps.filter(x => x.exists).map(enrichStamp)) setStamps(stamps.filter(x => x.exists).map(enrichStamp))
setLastUpdate(Date.now()) setLastUpdate(Date.now())
setError(null)
} catch (e) { } catch (e) {
setError(e as Error) setError(e as Error)
} finally { } finally {
+118
View File
@@ -0,0 +1,118 @@
import { useEffect, useState } from 'react'
import { CafeReactFsCreate } from './CafeReactFsCreate'
import { CafeReactFsItem } from './CafeReactFsItem'
import { CafeReactFsLoading } from './CafeReactFsLoading'
import { CafeReactFsPath } from './CafeReactFsPath'
import { CafeReactFsSync } from './CafeReactFsSync'
import { CafeReactFsUpload } from './CafeReactFsUpload'
import { FsItem } from './CafeReactType'
const DEFAULT_BACKGROUND_COLOR = '#f0f0f0'
interface Props {
download: (path: string) => Promise<void>
list: (path: string) => Promise<FsItem[]>
onUpload: (path: string) => Promise<void>
onCreateDirectory: (path: string) => Promise<void>
onDeleteFile: (path: string) => Promise<void>
onDeleteDirectory: (path: string) => Promise<void>
onSync: () => Promise<void>
reloader: number
backgroundColor?: string
rootAlias?: string
}
export function CafeReactFs({
download,
list,
onUpload,
onCreateDirectory,
onDeleteFile,
onDeleteDirectory,
onSync,
reloader,
backgroundColor,
rootAlias,
}: Props) {
const [path, setPath] = useState('/')
const [items, setItems] = useState<FsItem[]>([])
const [loading, setLoading] = useState(false)
function setItemsSorted(items: FsItem[]) {
// directories first, all alphabetically
const sortedItems = items.slice().sort((a, b) => {
if (a.$type === b.$type) {
return a.name.localeCompare(b.name)
}
return a.$type === 'directory' ? -1 : 1
})
setItems(sortedItems)
}
useEffect(() => {
setLoading(true)
list(path)
.then(setItemsSorted)
.finally(() => setLoading(false))
}, [reloader, list, path])
const pathParts = ['/', ...path.split('/').filter(x => x)]
function jumpToDirectory(fullPath: string) {
setPath(fullPath)
setLoading(true)
list(fullPath)
.then(setItemsSorted)
.finally(() => setLoading(false))
}
function enterDirectory(name: string) {
const newPath = path.endsWith('/') ? `${path}${name}` : `${path}/${name}`
setPath(newPath)
setLoading(true)
list(newPath)
.then(setItemsSorted)
.finally(() => setLoading(false))
}
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
<CafeReactFsPath
pathParts={pathParts}
jumpToDirectory={jumpToDirectory}
backgroundColor={backgroundColor ?? DEFAULT_BACKGROUND_COLOR}
rootAlias={rootAlias}
/>
<div style={{ display: 'flex', flexDirection: 'row', flexWrap: 'wrap', gap: '4px' }}>
{loading && <CafeReactFsLoading backgroundColor={backgroundColor ?? DEFAULT_BACKGROUND_COLOR} />}
{!loading &&
items.map(item => (
<CafeReactFsItem
key={item.id}
path={path}
item={item}
enterDirectory={enterDirectory}
onDeleteFile={onDeleteFile}
onDeleteDirectory={onDeleteDirectory}
download={download}
backgroundColor={backgroundColor ?? DEFAULT_BACKGROUND_COLOR}
/>
))}
{!loading && (
<>
<CafeReactFsUpload
onUpload={() => onUpload(path)}
backgroundColor={backgroundColor ?? DEFAULT_BACKGROUND_COLOR}
/>
<CafeReactFsCreate
onCreateDirectory={() => onCreateDirectory(path)}
backgroundColor={backgroundColor ?? DEFAULT_BACKGROUND_COLOR}
/>
<CafeReactFsSync backgroundColor={backgroundColor ?? DEFAULT_BACKGROUND_COLOR} onSync={onSync} />
</>
)}
</div>
</div>
)
}
+54
View File
@@ -0,0 +1,54 @@
import { useState } from 'react'
import { CafeReactFsLoading } from './CafeReactFsLoading'
interface Props {
backgroundColor: string
onCreateDirectory: () => Promise<void>
}
export function CafeReactFsCreate({ backgroundColor, onCreateDirectory }: Props) {
const [loading, setLoading] = useState(false)
function proxyUpload() {
setLoading(true)
onCreateDirectory().finally(() => setLoading(false))
}
if (loading) {
return <CafeReactFsLoading backgroundColor={backgroundColor} />
}
return (
<div
style={{
width: '80px',
height: '80px',
position: 'relative',
background: backgroundColor,
borderRadius: '2px',
cursor: 'pointer',
}}
onClick={proxyUpload}
>
<img
alt="Create"
style={{ width: '64px', height: '64px', position: 'absolute', left: '8px', top: 0 }}
src="data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3Csvg%20width%3D%22512%22%20height%3D%22512%22%20viewBox%3D%220%200%20512%20512%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M%20106%20131%20l%20100%200%20l%2025%2025%20l%20175%200%20l%200%20200%20l%20-300%200%20z%22%20stroke%3D%22%231F2D3D%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3Cpath%20d%3D%22M%20206%20256%20l%20100%200%22%20stroke%3D%22%23B83B5E%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3Cpath%20d%3D%22M%20256%20206%20l%200%20100%22%20stroke%3D%22%23B83B5E%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3C%2Fsvg%3E"
/>
<p
style={{
margin: 0,
fontFamily: 'sans-serif',
fontSize: '12px',
textAlign: 'center',
width: '80px',
position: 'absolute',
bottom: '5px',
left: 0,
}}
>
New Folder
</p>
</div>
)
}
+25
View File
@@ -0,0 +1,25 @@
interface Props {
onDelete: () => Promise<void>
}
export function CafeReactFsDelete({ onDelete }: Props) {
return (
<img
alt="Delete"
style={{
position: 'absolute',
top: '2px',
right: '2px',
width: '20px',
height: '20px',
cursor: 'pointer',
zIndex: 1,
}}
src="data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3Csvg%20width%3D%22128%22%20height%3D%22128%22%20viewBox%3D%220%200%20128%20128%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Ccircle%20cx%3D%2264%22%20cy%3D%2264%22%20r%3D%2264%22%20fill%3D%22%231F2D3D%22%20stroke%3D%22none%22%20stroke-width%3D%220%22%20%20%2F%3E%3Cpath%20d%3D%22M%2032%2064%20l%2064%200%22%20stroke%3D%22%23B83B5E%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3C%2Fsvg%3E"
onClick={event => {
onDelete()
event.stopPropagation()
}}
/>
)
}
+51
View File
@@ -0,0 +1,51 @@
import { useState } from 'react'
import { CafeReactFsDelete } from './CafeReactFsDelete'
import { CafeReactFsLoading } from './CafeReactFsLoading'
import { CafeReactFsName } from './CafeReactFsName'
import { VirtualDirectory } from './CafeReactType'
interface Props {
directory: VirtualDirectory
enterDirectory: (name: string) => void
deleteDirectory: (name: string) => Promise<void>
backgroundColor: string
}
export function CafeReactFsDirectory({ directory, enterDirectory, deleteDirectory, backgroundColor }: Props) {
const [hovered, setHovered] = useState(false)
const [loading, setLoading] = useState(false)
function proxyDelete() {
setLoading(true)
return deleteDirectory(directory.name).finally(() => setLoading(false))
}
if (loading) {
return <CafeReactFsLoading backgroundColor={backgroundColor} />
}
return (
<div
style={{
width: '80px',
height: '80px',
position: 'relative',
background: backgroundColor,
borderRadius: '2px',
cursor: 'pointer',
}}
onClick={() => enterDirectory(directory.name)}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
{hovered && <CafeReactFsDelete onDelete={proxyDelete} />}
<img
alt="Directory"
src="data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3Csvg%20width%3D%22512%22%20height%3D%22512%22%20viewBox%3D%220%200%20512%20512%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M%20106%20131%20l%20100%200%20l%2025%2025%20l%20175%200%20l%200%20200%20l%20-300%200%20z%22%20stroke%3D%22%231F2D3D%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3C%2Fsvg%3E"
style={{ width: '64px', height: '64px', position: 'absolute', left: '8px', top: 0 }}
/>
<CafeReactFsName name={directory.name} />
</div>
)
}
+53
View File
@@ -0,0 +1,53 @@
import { useState } from 'react'
import { CafeReactFsDelete } from './CafeReactFsDelete'
import { CafeReactFsLoading } from './CafeReactFsLoading'
import { CafeReactFsName } from './CafeReactFsName'
import { VirtualFile } from './CafeReactType'
import { joinUrl } from './Utility'
interface Props {
path: string
file: VirtualFile
download: (path: string) => Promise<void>
deleteFile: (path: string) => Promise<void>
backgroundColor: string
}
export function CafeReactFsFile({ path, file, download, deleteFile, backgroundColor }: Props) {
const [hovered, setHovered] = useState(false)
const [loading, setLoading] = useState(false)
if (loading) {
return <CafeReactFsLoading backgroundColor={backgroundColor} />
}
function proxyDelete() {
setLoading(true)
return deleteFile(joinUrl(path, file.name)).finally(() => setLoading(false))
}
return (
<div
onClick={() => download(joinUrl(path, file.name))}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
style={{
width: '80px',
height: '80px',
position: 'relative',
background: backgroundColor,
borderRadius: '2px',
cursor: 'pointer',
}}
>
{hovered && <CafeReactFsDelete onDelete={proxyDelete} />}
<img
alt="File"
style={{ width: '64px', height: '64px', position: 'absolute', left: '8px', top: 0 }}
src="data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3Csvg%20width%3D%22512%22%20height%3D%22512%22%20viewBox%3D%220%200%20512%20512%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M%20156%20131%20l%20150%200%20l%2050%2050%20l%200%20200%20l%20-200%200%20z%22%20stroke%3D%22%231F2D3D%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3Cpath%20d%3D%22M%20306%20131%20l%200%2050%20l%2050%200%22%20stroke%3D%22%231F2D3D%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3C%2Fsvg%3E"
/>
<CafeReactFsName name={file.name} />
</div>
)
}
+48
View File
@@ -0,0 +1,48 @@
import { CafeReactFsDirectory } from './CafeReactFsDirectory'
import { CafeReactFsFile } from './CafeReactFsFile'
import { FsItem, isVirtualDirectory, isVirtualFile } from './CafeReactType'
interface Props {
path: string
item: FsItem
download: (path: string) => Promise<void>
enterDirectory: (name: string) => void
onDeleteFile: (path: string) => Promise<void>
onDeleteDirectory: (path: string) => Promise<void>
backgroundColor: string
}
export function CafeReactFsItem({
path,
item,
download,
enterDirectory,
onDeleteFile,
onDeleteDirectory,
backgroundColor,
}: Props) {
if (isVirtualFile(item)) {
return (
<CafeReactFsFile
path={path}
file={item}
download={download}
deleteFile={onDeleteFile}
backgroundColor={backgroundColor}
/>
)
}
if (isVirtualDirectory(item)) {
return (
<CafeReactFsDirectory
directory={item}
enterDirectory={enterDirectory}
deleteDirectory={onDeleteDirectory}
backgroundColor={backgroundColor}
/>
)
}
return null
}
+52
View File
@@ -0,0 +1,52 @@
import { CSSProperties } from 'react'
interface Props {
backgroundColor: string
}
export function CafeReactFsLoading({ backgroundColor }: Props) {
const spinnerStyle = {
width: '80px',
height: '80px',
borderRadius: '2px',
position: 'relative',
background: backgroundColor,
} as CSSProperties
const bounceStyle = {
width: '32px',
height: '32px',
borderRadius: '50%',
backgroundColor: '#333',
top: '24px',
left: '24px',
opacity: 0.6,
position: 'absolute',
animation: 'bounce 2.0s infinite ease-in-out',
} as CSSProperties
const bounceStyle2 = {
...bounceStyle,
animationDelay: '-1.0s',
}
const keyframes = `
@keyframes bounce {
0%, 100% {
transform: scale(0.0);
} 50% {
transform: scale(1.0);
}
}
`
return (
<>
<style>{keyframes}</style>
<div style={spinnerStyle}>
<div style={bounceStyle}></div>
<div style={bounceStyle2}></div>
</div>
</>
)
}
+25
View File
@@ -0,0 +1,25 @@
interface Props {
name: string
}
export function CafeReactFsName({ name }: Props) {
const shortName = name.length > 10 ? name.slice(0, 10) + '...' : name
return (
<p
title={name}
style={{
margin: 0,
fontFamily: 'sans-serif',
fontSize: '12px',
textAlign: 'center',
width: '80px',
position: 'absolute',
bottom: '5px',
left: 0,
}}
>
{shortName}
</p>
)
}
+44
View File
@@ -0,0 +1,44 @@
import { joinUrl } from './Utility'
interface Props {
pathParts: string[]
jumpToDirectory: (fullPath: string) => void
backgroundColor: string
rootAlias?: string
}
export function CafeReactFsPath({ pathParts, jumpToDirectory, backgroundColor, rootAlias }: Props) {
const absolutePaths: string[] = []
for (const pathPart of pathParts) {
if (absolutePaths.length === 0) {
absolutePaths.push(pathPart)
} else {
absolutePaths.push(joinUrl(absolutePaths[absolutePaths.length - 1], pathPart))
}
}
return (
<div style={{ display: 'flex', flexDirection: 'row', flexWrap: 'wrap', gap: '4px' }}>
{pathParts.map((part, index) => (
<button
key={index}
style={{
background: backgroundColor,
borderRadius: '2px',
cursor: 'pointer',
outline: 'none',
border: 'none',
fontSize: '20px',
padding: '4px 16px',
}}
onClick={() => {
jumpToDirectory(absolutePaths[index])
}}
>
{rootAlias && part === '/' ? rootAlias : part}
</button>
))}
</div>
)
}
+40
View File
@@ -0,0 +1,40 @@
interface Props {
backgroundColor: string
onSync: () => Promise<void>
}
export function CafeReactFsSync({ backgroundColor, onSync }: Props) {
return (
<div
style={{
width: '80px',
height: '80px',
position: 'relative',
background: backgroundColor,
borderRadius: '2px',
cursor: 'pointer',
}}
onClick={onSync}
>
<img
alt="Sync"
style={{ width: '64px', height: '64px', position: 'absolute', left: '8px', top: 0 }}
src="data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3Csvg%20width%3D%22512%22%20height%3D%22512%22%20viewBox%3D%220%200%20512%20512%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M%20156%20156%20l%20250%200%20l%200%20150%22%20stroke%3D%22%231F2D3D%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3Cpath%20d%3D%22M%20356%20256%20l%2050%2050%20l%2050%20-50%22%20stroke%3D%22%231F2D3D%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3Cpath%20d%3D%22M%20356%20356%20l%20-250%200%20l%200%20-150%22%20stroke%3D%22%231F2D3D%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3Cpath%20d%3D%22M%20156%20256%20l%20-50%20-50%20l%20-50%2050%22%20stroke%3D%22%231F2D3D%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3C%2Fsvg%3E"
/>
<p
style={{
margin: 0,
fontFamily: 'sans-serif',
fontSize: '12px',
textAlign: 'center',
width: '80px',
position: 'absolute',
bottom: '5px',
left: 0,
}}
>
Sync
</p>
</div>
)
}
+54
View File
@@ -0,0 +1,54 @@
import { useState } from 'react'
import { CafeReactFsLoading } from './CafeReactFsLoading'
interface Props {
onUpload: () => Promise<void>
backgroundColor: string
}
export function CafeReactFsUpload({ onUpload, backgroundColor }: Props) {
const [uploading, setUploading] = useState(false)
function proxyUpload() {
setUploading(true)
onUpload().finally(() => setUploading(false))
}
if (uploading) {
return <CafeReactFsLoading backgroundColor={backgroundColor} />
}
return (
<div
style={{
width: '80px',
height: '80px',
position: 'relative',
background: backgroundColor,
borderRadius: '2px',
cursor: 'pointer',
}}
onClick={proxyUpload}
>
<img
alt="Upload"
style={{ width: '64px', height: '64px', position: 'absolute', left: '8px', top: 0 }}
src="data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3Csvg%20width%3D%22512%22%20height%3D%22512%22%20viewBox%3D%220%200%20512%20512%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M%20156%20131%20l%20150%200%20l%2050%2050%20l%200%20200%20l%20-200%200%20z%22%20stroke%3D%22%231F2D3D%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3Cpath%20d%3D%22M%20306%20131%20l%200%2050%20l%2050%200%22%20stroke%3D%22%231F2D3D%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3Cpath%20d%3D%22M%20206%20256%20l%20100%200%22%20stroke%3D%22%23B83B5E%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3Cpath%20d%3D%22M%20256%20206%20l%200%20100%22%20stroke%3D%22%23B83B5E%22%20stroke-width%3D%2220%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%20%2F%3E%3C%2Fsvg%3E"
/>
<p
style={{
margin: 0,
fontFamily: 'sans-serif',
fontSize: '12px',
textAlign: 'center',
width: '80px',
position: 'absolute',
bottom: '5px',
left: 0,
}}
>
Upload
</p>
</div>
)
}
+26
View File
@@ -0,0 +1,26 @@
export enum FsItemType {
FILE = 'file',
DIRECTORY = 'directory',
}
export interface VirtualFile {
id: string | number
name: string
$type: FsItemType.FILE
}
export interface VirtualDirectory {
id: string | number
name: string
$type: FsItemType.DIRECTORY
}
export type FsItem = VirtualFile | VirtualDirectory
export function isVirtualFile(item: FsItem): item is VirtualFile {
return item.$type === FsItemType.FILE
}
export function isVirtualDirectory(item: FsItem): item is VirtualDirectory {
return item.$type === FsItemType.DIRECTORY
}
+6
View File
@@ -0,0 +1,6 @@
export function joinUrl(...parts: unknown[]): string {
return parts
.filter(x => x)
.join('/')
.replace(/(?<!:)\/+/g, '/')
}
+4 -1
View File
@@ -5,6 +5,7 @@ import { AccountFeeds } from './pages/account/feeds/AccountFeeds'
import { AccountStaking } from './pages/account/staking/AccountStaking' import { AccountStaking } from './pages/account/staking/AccountStaking'
import { AccountStamps } from './pages/account/stamps/AccountStamps' import { AccountStamps } from './pages/account/stamps/AccountStamps'
import { AccountWallet } from './pages/account/wallet/AccountWallet' import { AccountWallet } from './pages/account/wallet/AccountWallet'
import FDP from './pages/fdp'
import CreateNewFeed from './pages/feeds/CreateNewFeed' import CreateNewFeed from './pages/feeds/CreateNewFeed'
import { FeedSubpage } from './pages/feeds/FeedSubpage' import { FeedSubpage } from './pages/feeds/FeedSubpage'
import UpdateFeed from './pages/feeds/UpdateFeed' import UpdateFeed from './pages/feeds/UpdateFeed'
@@ -17,6 +18,7 @@ import Info from './pages/info'
import LightModeRestart from './pages/restart/LightModeRestart' import LightModeRestart from './pages/restart/LightModeRestart'
import Settings from './pages/settings' import Settings from './pages/settings'
import { CreatePostageStampPage } from './pages/stamps/CreatePostageStampAdvancedPage' import { CreatePostageStampPage } from './pages/stamps/CreatePostageStampAdvancedPage'
import { CreatePostageStampBasicPage } from './pages/stamps/CreatePostageStampStandardPage'
import Status from './pages/status' import Status from './pages/status'
import TopUp from './pages/top-up' import TopUp from './pages/top-up'
import { BankCardTopUpIndex } from './pages/top-up/BankCardTopUpIndex' import { BankCardTopUpIndex } from './pages/top-up/BankCardTopUpIndex'
@@ -25,7 +27,6 @@ import { GiftCardFund } from './pages/top-up/GiftCardFund'
import { GiftCardTopUpIndex } from './pages/top-up/GiftCardTopUpIndex' import { GiftCardTopUpIndex } from './pages/top-up/GiftCardTopUpIndex'
import { Swap } from './pages/top-up/Swap' import { Swap } from './pages/top-up/Swap'
import { Context as SettingsContext } from './providers/Settings' import { Context as SettingsContext } from './providers/Settings'
import { CreatePostageStampBasicPage } from './pages/stamps/CreatePostageStampStandardPage'
export enum ROUTES { export enum ROUTES {
INFO = '/', INFO = '/',
@@ -55,6 +56,7 @@ export enum ROUTES {
ACCOUNT_FEEDS_VIEW = '/account/feeds/:uuid', ACCOUNT_FEEDS_VIEW = '/account/feeds/:uuid',
ACCOUNT_INVITATIONS = '/account/invitations', ACCOUNT_INVITATIONS = '/account/invitations',
ACCOUNT_STAKING = '/account/staking', ACCOUNT_STAKING = '/account/staking',
FDP = '/fdp',
} }
export const ACCOUNT_TABS = [ export const ACCOUNT_TABS = [
@@ -95,6 +97,7 @@ const BaseRouter = (): ReactElement => {
<Route path={ROUTES.ACCOUNT_FEEDS_UPDATE} element={<UpdateFeed />} /> <Route path={ROUTES.ACCOUNT_FEEDS_UPDATE} element={<UpdateFeed />} />
<Route path={ROUTES.ACCOUNT_FEEDS_VIEW} element={<FeedSubpage />} /> <Route path={ROUTES.ACCOUNT_FEEDS_VIEW} element={<FeedSubpage />} />
<Route path={ROUTES.ACCOUNT_STAKING} element={<AccountStaking />} /> <Route path={ROUTES.ACCOUNT_STAKING} element={<AccountStaking />} />
<Route path={ROUTES.FDP} element={<FDP />} />
{isDesktop && <Route path={ROUTES.ACCOUNT_INVITATIONS} element={<GiftCards />} />} {isDesktop && <Route path={ROUTES.ACCOUNT_INVITATIONS} element={<GiftCards />} />}
</Routes> </Routes>
) )
+1 -2
View File
@@ -31,8 +31,7 @@ export function makeBigNumber(value: BigNumber | bigint | number | string): BigN
if (typeof value === 'bigint') return new BigNumber(value.toString()) if (typeof value === 'bigint') return new BigNumber(value.toString())
// FIXME: bee-js still returns some values as numbers and even outside of SAFE INTEGER bounds if (typeof value === 'number') return new BigNumber(value)
if (typeof value === 'number' /* && Number.isSafeInteger(value)*/) return new BigNumber(value)
throw new TypeError(`Not a BigNumber or BigNumber convertible value. Type: ${typeof value} value: ${value}`) throw new TypeError(`Not a BigNumber or BigNumber convertible value. Type: ${typeof value} value: ${value}`)
} }
+1 -1
View File
@@ -19,7 +19,7 @@ export async function sendRequest(
const authorization = localStorage.getItem('apiKey') const authorization = localStorage.getItem('apiKey')
if (!authorization) { if (!authorization) {
throw Error('API key not found in local storage') throw Error('API key not found in local storage. Please reopen via Swarm Desktop > Open Web UI.')
} }
const headers = { const headers = {
authorization, authorization,
+7 -1
View File
@@ -66,7 +66,13 @@ export async function sendNativeTransaction(
): Promise<TransferResponse> { ): Promise<TransferResponse> {
const signer = await makeReadySigner(privateKey, jsonRpcProvider) const signer = await makeReadySigner(privateKey, jsonRpcProvider)
const gasPrice = externalGasPrice ?? (await signer.getGasPrice()) const gasPrice = externalGasPrice ?? (await signer.getGasPrice())
const transaction = await signer.sendTransaction({ to, value, gasPrice }) const transaction = await signer.sendTransaction({
to,
value: BN.from(value),
gasPrice,
gasLimit: BN.from(21000),
type: 0,
})
const receipt = await transaction.wait(1) const receipt = await transaction.wait(1)
return { transaction, receipt } return { transaction, receipt }
+2
View File
@@ -18,7 +18,9 @@ async function getData(url) {
function processData(data) { function processData(data) {
const db = new Map() const db = new Map()
data.nodes.forEach(node => { data.nodes.forEach(node => {
if (node.location) {
db.set(node.overlay, { lat: node.location.latitude, lng: node.location.longitude }) db.set(node.overlay, { lat: node.location.latitude, lng: node.location.longitude })
}
}) })
return Object.fromEntries([...db.entries()].sort()) return Object.fromEntries([...db.entries()].sort())