Compare commits

...

8 Commits

Author SHA1 Message Date
bee-worker 16ffffb0c4 chore(master): release 0.25.0 (#637) 2023-12-04 22:32:19 +01:00
Cafe137 080d9f2c2a refactor: clean up postage stamp screens (#642)
* refactor: clean up postage stamp screens

* fix: add immutable flag
2023-12-04 22:28:44 +01:00
rolandlor 4f9abc614e feat: update postage stamp creation screen (#641)
* style: UI changes for postage stamp

* feat: New postage stamp standard page

---------

Co-authored-by: Seres Roland <seresroland@Seres-MBP.home>
2023-12-04 22:01:51 +01:00
Cafe137 20a051b658 fix: put stamp input error handling in state (#640) 2023-11-20 14:59:00 +01:00
Ferenc Sárai 0c2ac0c454 feat: improve topup and dilute ux
Co-authored-by: Ferenc Sárai <ferenc.sarai@solarpunk.buzz>
2023-11-20 14:00:42 +01:00
Ferenc Sárai 8802d20555 style: add padding to files download (#387) (#639)
Co-authored-by: Ferenc Sárai <ferenc.sarai@solarpunk.buzz>
2023-11-20 13:59:40 +01:00
zol1981 7fa1cb0ccf fix: add missing stamp labels and fix inputs (#634)
* fix issue #630, #606

* fix: Stamp Labels #630, Entering * into amount #606

* #606 Entering * into amount

* fix: inputs eliminating warnings

---------

Co-authored-by: Zoltán Mihály <zolmac@Zoltans-MacBook-Pro.local>
2023-11-08 14:15:34 +01:00
Ferenc Sárai bab08e1df2 style: add padding to account chequebook (#635)
Co-authored-by: Ferenc Sárai <ferenc.sarai@solarpunk.buzz>
2023-11-07 12:33:28 +01:00
18 changed files with 467 additions and 131 deletions
+14
View File
@@ -1,5 +1,19 @@
# Changelog
## [0.25.0](https://github.com/ethersphere/bee-dashboard/compare/v0.24.1...v0.25.0) (2023-12-04)
### Features
* improve topup and dilute ux ([0c2ac0c](https://github.com/ethersphere/bee-dashboard/commit/0c2ac0c454ad02200a2762958c5bc5abbdfe8005))
* update postage stamp creation screen ([#641](https://github.com/ethersphere/bee-dashboard/issues/641)) ([4f9abc6](https://github.com/ethersphere/bee-dashboard/commit/4f9abc614eedd5ce3a279a4686cc832c4d1e62c7))
### Bug Fixes
* add missing stamp labels and fix inputs ([#634](https://github.com/ethersphere/bee-dashboard/issues/634)) ([7fa1cb0](https://github.com/ethersphere/bee-dashboard/commit/7fa1cb0ccf9f2a32263e84aa76732ebd2fc7fb22))
* put stamp input error handling in state ([#640](https://github.com/ethersphere/bee-dashboard/issues/640)) ([20a051b](https://github.com/ethersphere/bee-dashboard/commit/20a051b6589c22397a7305d722a56df0604ff7a4))
## [0.24.1](https://github.com/ethersphere/bee-dashboard/compare/v0.24.0...v0.24.1) (2023-10-18)
+25 -25
View File
@@ -1,15 +1,15 @@
{
"name": "@ethersphere/bee-dashboard",
"version": "0.24.1",
"version": "0.25.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ethersphere/bee-dashboard",
"version": "0.24.1",
"version": "0.25.0",
"license": "BSD-3-Clause",
"dependencies": {
"@ethersphere/bee-js": "^6.2.0",
"@ethersphere/bee-js": "^6.7.0",
"@ethersphere/swarm-cid": "^0.1.0",
"@material-ui/core": "4.12.3",
"@material-ui/icons": "4.11.2",
@@ -2440,13 +2440,13 @@
}
},
"node_modules/@ethersphere/bee-js": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-6.2.0.tgz",
"integrity": "sha512-QWuVvW+c9z+AFWuRL+YBZECDyV3G5qeD9L0J7Tx7HYMugoDjMAjeRyNHjkf0a5np57q3i9MAE2P678rjSJ1Yaw==",
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-6.7.0.tgz",
"integrity": "sha512-t1bsUj9BmICuRL6XENTVyZZCfkFuCjc6pQxOekuVFLBd0Qpmyf87iRpVizvZN5IKhVqqw9xCzkg8otJKQdAKNA==",
"dependencies": {
"@ethersphere/swarm-cid": "^0.1.0",
"@types/readable-stream": "^2.3.13",
"axios": "^1.3.4",
"axios": "^0.27.2",
"cafe-utility": "^10.8.1",
"elliptic": "^6.5.4",
"fetch-blob": "2.1.2",
@@ -2466,13 +2466,12 @@
}
},
"node_modules/@ethersphere/bee-js/node_modules/axios": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
}
},
"node_modules/@ethersphere/bee-js/node_modules/form-data": {
@@ -16491,7 +16490,8 @@
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"dev": true
},
"node_modules/psl": {
"version": "1.8.0",
@@ -22305,13 +22305,13 @@
}
},
"@ethersphere/bee-js": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-6.2.0.tgz",
"integrity": "sha512-QWuVvW+c9z+AFWuRL+YBZECDyV3G5qeD9L0J7Tx7HYMugoDjMAjeRyNHjkf0a5np57q3i9MAE2P678rjSJ1Yaw==",
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-6.7.0.tgz",
"integrity": "sha512-t1bsUj9BmICuRL6XENTVyZZCfkFuCjc6pQxOekuVFLBd0Qpmyf87iRpVizvZN5IKhVqqw9xCzkg8otJKQdAKNA==",
"requires": {
"@ethersphere/swarm-cid": "^0.1.0",
"@types/readable-stream": "^2.3.13",
"axios": "^1.3.4",
"axios": "^0.27.2",
"cafe-utility": "^10.8.1",
"elliptic": "^6.5.4",
"fetch-blob": "2.1.2",
@@ -22324,13 +22324,12 @@
},
"dependencies": {
"axios": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"requires": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
}
},
"form-data": {
@@ -32620,7 +32619,8 @@
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"dev": true
},
"psl": {
"version": "1.8.0",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ethersphere/bee-dashboard",
"version": "0.24.1",
"version": "0.25.0",
"description": "An app which helps users to setup their Bee node and do actions like cash out cheques",
"keywords": [
"bee",
@@ -26,7 +26,7 @@
"url": "https://github.com/ethersphere/bee-dashboard.git"
},
"dependencies": {
"@ethersphere/bee-js": "^6.2.0",
"@ethersphere/bee-js": "^6.7.0",
"@ethersphere/swarm-cid": "^0.1.0",
"@material-ui/core": "4.12.3",
"@material-ui/icons": "4.11.2",
+3
View File
@@ -24,6 +24,9 @@ const useStyles = makeStyles((theme: Theme) =>
},
contentLevel12: {
marginTop: theme.spacing(0.25),
'& > li:last-of-type': {
marginBottom: theme.spacing(2),
},
},
infoText: {
color: '#c9c9c9',
+28 -26
View File
@@ -1,4 +1,4 @@
import { Grid, IconButton, InputBase, ListItem, Typography } from '@material-ui/core'
import { Box, Grid, IconButton, InputBase, ListItem, Typography } from '@material-ui/core'
import Collapse from '@material-ui/core/Collapse'
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
import { ChangeEvent, ReactElement, useState } from 'react'
@@ -134,31 +134,33 @@ export default function ExpandableListItemKey({
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
{helperText && <ExpandableListItemNote>{helperText}</ExpandableListItemNote>}
<ExpandableListItemActions>
<SwarmButton
disabled={
loading ||
inputValue === value ||
Boolean(confirmLabelDisabled) || // Disable if external validation is provided
(inputValue === '' && value === undefined) // Disable if no initial value was not provided and the field is empty. The undefined check is improtant so that it is possible to submit with empty input in other cases
}
loading={loading}
iconType={confirmIcon ?? Check}
onClick={() => {
if (onConfirm) onConfirm(inputValue)
}}
>
{confirmLabel || 'Save'}
</SwarmButton>
<SwarmButton
disabled={loading || inputValue === value || inputValue === ''}
iconType={X}
onClick={() => setInputValue(value || '')}
cancel
>
Cancel
</SwarmButton>
</ExpandableListItemActions>
<Box mt={2}>
<ExpandableListItemActions>
<SwarmButton
disabled={
loading ||
inputValue === value ||
Boolean(confirmLabelDisabled) || // Disable if external validation is provided
(inputValue === '' && value === undefined) // Disable if no initial value was not provided and the field is empty. The undefined check is improtant so that it is possible to submit with empty input in other cases
}
loading={loading}
iconType={confirmIcon ?? Check}
onClick={() => {
if (onConfirm) onConfirm(inputValue)
}}
>
{confirmLabel || 'Save'}
</SwarmButton>
<SwarmButton
disabled={loading || inputValue === value || inputValue === ''}
iconType={X}
onClick={() => setInputValue(value || '')}
cancel
>
Cancel
</SwarmButton>
</ExpandableListItemActions>
</Box>
</Collapse>
</>
)
+8 -17
View File
@@ -1,4 +1,5 @@
import { BeeDebug } from '@ethersphere/bee-js'
import { Box } from '@material-ui/core'
import Button from '@material-ui/core/Button'
import Dialog from '@material-ui/core/Dialog'
import DialogActions from '@material-ui/core/DialogActions'
@@ -7,20 +8,19 @@ import DialogTitle from '@material-ui/core/DialogTitle'
import Input from '@material-ui/core/Input'
import { useSnackbar } from 'notistack'
import { ReactElement, ReactNode, useState } from 'react'
import { SwarmSelect } from './SwarmSelect'
interface Props {
label: string
type: 'Topup' | 'Dilute'
icon: ReactNode
beeDebug: BeeDebug
stamp: string
}
export default function StampExtensionModal({ label, icon, beeDebug, stamp }: Props): ReactElement {
export default function StampExtensionModal({ type, icon, beeDebug, stamp }: Props): ReactElement {
const [open, setOpen] = useState(false)
const [amount, setAmount] = useState('')
const [type, setType] = useState<'Topup' | 'Dilute'>('Topup')
const { enqueueSnackbar } = useSnackbar()
const label = `${type} ${stamp.substring(0, 8)}`
const handleClickOpen = (e: React.MouseEvent<HTMLButtonElement>) => {
setOpen(true)
@@ -56,22 +56,13 @@ export default function StampExtensionModal({ label, icon, beeDebug, stamp }: Pr
}
return (
<div>
<Box mb={2}>
<Button variant="contained" onClick={handleClickOpen} startIcon={icon}>
{label}
{type}
</Button>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">{label}</DialogTitle>
<DialogContent>
<SwarmSelect
label="Action"
defaultValue="Topup"
onChange={event => setType(event.target.value as 'Topup' | 'Dilute')}
options={[
{ value: 'Topup', label: 'Topup' },
{ value: 'Dilute', label: 'Dilute' },
]}
/>
<Input
autoFocus
margin="dense"
@@ -87,11 +78,11 @@ export default function StampExtensionModal({ label, icon, beeDebug, stamp }: Pr
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleAction} color="primary">
<Button disabled={amount === ''} onClick={handleAction} color="primary">
{type}
</Button>
</DialogActions>
</Dialog>
</div>
</Box>
)
}
@@ -12,6 +12,7 @@ import { Context as SettingsContext } from '../../../providers/Settings'
import PeerBalances from '../../accounting/PeerBalances'
import { AccountNavigation } from '../AccountNavigation'
import { Header } from '../Header'
import { Box } from '@material-ui/core'
export function AccountChequebook(): ReactElement {
const { status, nodeAddresses, chequebookAddress, chequebookBalance, settlements, peerBalances } =
@@ -43,10 +44,12 @@ export function AccountChequebook(): ReactElement {
label="Total Cheques Amount Sent"
value={`${settlements?.totalSent.toFixedDecimal()} xBZZ`}
/>
<ExpandableListItem
label="Total Cheques Amount Received"
value={`${settlements?.totalReceived.toFixedDecimal()} xBZZ`}
/>
<Box mb={2}>
<ExpandableListItem
label="Total Cheques Amount Received"
value={`${settlements?.totalReceived.toFixedDecimal()} xBZZ`}
/>
</Box>
<ExpandableListItemActions>
<WithdrawModal />
<DepositModal />
+1 -1
View File
@@ -46,7 +46,7 @@ export function AccountStamps(): ReactElement {
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
function navigateToNewStamp() {
navigate(ROUTES.ACCOUNT_STAMPS_NEW)
navigate(ROUTES.ACCOUNT_STAMPS_NEW_STANDARD)
}
return (
+2 -2
View File
@@ -18,7 +18,7 @@ import { detectIndexHtml, getAssetNameFromFiles, packageFile } from '../../utils
import { persistIdentity, updateFeed } from '../../utils/identity'
import { HISTORY_KEYS, putHistory } from '../../utils/local-storage'
import { FeedPasswordDialog } from '../feeds/FeedPasswordDialog'
import { PostageStampCreation } from '../stamps/PostageStampCreation'
import { PostageStampAdvancedCreation } from '../stamps/PostageStampAdvancedCreation'
import { PostageStampSelector } from '../stamps/PostageStampSelector'
import { AssetPreview } from './AssetPreview'
import { StampPreview } from './StampPreview'
@@ -186,7 +186,7 @@ export function Upload(): ReactElement {
{hasAnyStamps && stampMode === 'SELECT' ? (
<PostageStampSelector onSelect={stamp => setStamp(stamp)} defaultValue={stamp?.batchID} />
) : (
<PostageStampCreation onFinished={() => setStampMode('SELECT')} />
<PostageStampAdvancedCreation onFinished={() => setStampMode('SELECT')} />
)}
</Box>
<Box mb={4}>
@@ -2,7 +2,7 @@ import { ReactElement } from 'react'
import { useNavigate } from 'react-router'
import { HistoryHeader } from '../../components/HistoryHeader'
import { ROUTES } from '../../routes'
import { PostageStampCreation } from './PostageStampCreation'
import { PostageStampAdvancedCreation } from './PostageStampAdvancedCreation'
export function CreatePostageStampPage(): ReactElement {
const navigate = useNavigate()
@@ -13,8 +13,8 @@ export function CreatePostageStampPage(): ReactElement {
return (
<div>
<HistoryHeader>Buy new postage stamp</HistoryHeader>
<PostageStampCreation onFinished={onFinished} />
<HistoryHeader>Buy new postage stamp batch</HistoryHeader>
<PostageStampAdvancedCreation onFinished={onFinished} />
</div>
)
}
@@ -0,0 +1,20 @@
import { ReactElement } from 'react'
import { useNavigate } from 'react-router'
import { HistoryHeader } from '../../components/HistoryHeader'
import { ROUTES } from '../../routes'
import { PostageStampStandardCreation } from './PostageStampStandardCreation'
export function CreatePostageStampBasicPage(): ReactElement {
const navigate = useNavigate()
function onFinished() {
navigate(ROUTES.ACCOUNT_STAMPS)
}
return (
<div>
<HistoryHeader>Buy new postage stamp batch</HistoryHeader>
<PostageStampStandardCreation onFinished={onFinished} />
</div>
)
}
@@ -1,8 +1,9 @@
import { PostageBatchOptions } from '@ethersphere/bee-js'
import { Box, Grid, Typography } from '@material-ui/core'
import { Box, Grid, Typography, createStyles, makeStyles } from '@material-ui/core'
import BigNumber from 'bignumber.js'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useEffect, useState } from 'react'
import { ReactElement, useContext, useState } from 'react'
import { Link } from 'react-router-dom'
import Check from 'remixicon-react/CheckLineIcon'
import { SwarmButton } from '../../components/SwarmButton'
import { SwarmSelect } from '../../components/SwarmSelect'
@@ -10,6 +11,7 @@ import { SwarmTextInput } from '../../components/SwarmTextInput'
import { Context as BeeContext } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings'
import { Context as StampsContext } from '../../providers/Stamps'
import { ROUTES } from '../../routes'
import {
calculateStampPrice,
convertAmountToSeconds,
@@ -23,7 +25,25 @@ interface Props {
onFinished: () => void
}
export function PostageStampCreation({ onFinished }: Props): ReactElement {
const useStyles = makeStyles(() =>
createStyles({
link: {
color: '#dd7700',
textDecoration: 'underline',
'&:hover': {
textDecoration: 'none',
// https://github.com/mui-org/material-ui/issues/22543
'@media (hover: none)': {
textDecoration: 'none',
},
},
},
}),
)
export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElement {
const classes = useStyles()
const { chainState } = useContext(BeeContext)
const { refresh } = useContext(StampsContext)
const { beeDebugApi } = useContext(SettingsContext)
@@ -32,7 +52,8 @@ export function PostageStampCreation({ onFinished }: Props): ReactElement {
const [amountInput, setAmountInput] = useState<string>('')
const [labelInput, setLabelInput] = useState('')
const [immutable, setImmutable] = useState(false)
const [errors, setErrors] = useState<Record<string, string>>({})
const [depthError, setDepthError] = useState<string>('')
const [amountError, setAmountError] = useState<string>('')
const [submitting, setSubmitting] = useState(false)
const { enqueueSnackbar } = useSnackbar()
@@ -102,41 +123,53 @@ export function PostageStampCreation({ onFinished }: Props): ReactElement {
setSubmitting(false)
}
useEffect(() => {
function validate() {
const errors: Record<string, string> = {}
function validateAmountInput(amountInput: string) {
let validAmountInput = '0'
if (!depthInput) {
errors.depth = 'Required field'
} else {
const depth = new BigNumber(depthInput)
if (!depth.isInteger()) {
errors.depth = 'Depth must be an integer'
} else if (depth.isLessThan(17)) {
errors.depth = 'Minimal depth is 17'
} else if (depth.isGreaterThan(255)) {
errors.depth = 'Depth has to be at most 255'
}
}
if (!amountInput) {
errors.amount = 'Required field'
if (!amountInput) {
setAmountError('Required field')
} else {
if (amountInput.indexOf('.') > -1) {
setAmountError('Amount must be an integer')
} else {
const amount = new BigNumber(amountInput)
if (!amount.isInteger()) {
errors.amount = 'Amount must be an integer'
if (amount.isNaN()) {
setAmountError('Amount must contain only digits')
} else if (amount.isLessThanOrEqualTo(0)) {
errors.amount = 'Amount must be greater than 0'
setAmountError('Amount must be greater than 0')
} else {
setAmountError('')
validAmountInput = amountInput
}
}
return errors
}
setErrors(validate())
}, [depthInput, amountInput])
setAmountInput(validAmountInput)
}
function validateDepthInput(depthInput: string) {
let validDepthInput = '0'
if (!depthInput) {
setDepthError('Required field')
} else {
const depth = new BigNumber(depthInput)
if (!depth.isInteger()) {
setDepthError('Depth must be an integer')
} else if (depth.isLessThan(17)) {
setDepthError('Minimal depth is 17')
} else if (depth.isGreaterThan(255)) {
setDepthError('Depth has to be at most 255')
} else {
setDepthError('')
validDepthInput = depthInput
}
}
setDepthInput(validDepthInput)
}
return (
<>
@@ -155,22 +188,24 @@ export function PostageStampCreation({ onFinished }: Props): ReactElement {
</Typography>
</Box>
<Box mb={2}>
<SwarmTextInput name="depth" label="Depth" onChange={event => setDepthInput(event.target.value)} />
<SwarmTextInput name="depth" label="Depth" onChange={event => validateDepthInput(event.target.value)} />
<Box mt={0.25} sx={{ bgcolor: '#f6f6f6' }} p={2}>
<Grid container justifyContent="space-between">
<Typography>Corresponding file size</Typography>
<Typography>{!errors.depth && depthInput ? getFileSize(parseInt(depthInput, 10)) : '-'}</Typography>
<Typography>{!depthError && depthInput ? getFileSize(parseInt(depthInput, 10)) : '-'}</Typography>
</Grid>
</Box>
{depthError && <Typography>{depthError}</Typography>}
</Box>
<Box mb={2}>
<SwarmTextInput name="amount" label="Amount" onChange={event => setAmountInput(event.target.value)} />
<SwarmTextInput name="amount" label="Amount" onChange={event => validateAmountInput(event.target.value)} />
<Box mt={0.25} sx={{ bgcolor: '#f6f6f6' }} p={2}>
<Grid container justifyContent="space-between">
<Typography>Corresponding TTL (Time to live)</Typography>
<Typography>{!errors.amount && amountInput ? getTtl(Number.parseInt(amountInput, 10)) : '-'}</Typography>
<Typography>{!amountError && amountInput ? getTtl(Number.parseInt(amountInput, 10)) : '-'}</Typography>
</Grid>
</Box>
{amountError && <Typography>{amountError}</Typography>}
</Box>
<Box mb={2}>
<SwarmTextInput name="label" label="Label" optional onChange={event => setLabelInput(event.target.value)} />
@@ -206,20 +241,30 @@ export function PostageStampCreation({ onFinished }: Props): ReactElement {
<Grid container justifyContent="space-between">
<Typography>Indicative Price</Typography>
<Typography>
{!errors.amount && !errors.depth && amountInput && depthInput
{!amountError && !depthError && amountInput && depthInput
? getPrice(parseInt(depthInput, 10), BigInt(amountInput))
: '-'}
</Typography>
</Grid>
</Box>
<SwarmButton
disabled={submitting || Object.keys(errors).length > 0}
onClick={submit}
iconType={Check}
loading={submitting}
>
Buy New Stamp
</SwarmButton>
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<SwarmButton
disabled={submitting || Boolean(depthError) || Boolean(amountError) || !depthInput || !amountInput}
onClick={submit}
iconType={Check}
loading={submitting}
>
Buy New Stamp
</SwarmButton>
</Grid>
<Grid item>
<Link to={ROUTES.ACCOUNT_STAMPS_NEW_STANDARD} className={classes.link}>
Standard mode
</Link>
</Grid>
</Grid>
</>
)
}
+4 -1
View File
@@ -23,7 +23,10 @@ export function PostageStampSelector({ onSelect, defaultValue }: Props): ReactEl
return (
<SwarmSelect
options={(stamps || []).map(x => ({ label: x.batchID.slice(0, 8), value: x.batchID }))}
options={(stamps || []).map(x => ({
label: x.label ? x.batchID.slice(0, 8) + ' - ' + x.label : x.batchID.slice(0, 8),
value: x.batchID,
}))}
onChange={event => onChange(event.target.value as string)}
defaultValue={defaultValue}
placeholder="Please select a postage stamp..."
@@ -0,0 +1,228 @@
import { PostageBatchOptions, Utils } from '@ethersphere/bee-js'
import { Box, Button, Grid, Slider, Typography } from '@material-ui/core'
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useState } from 'react'
import { Link } from 'react-router-dom'
import Check from 'remixicon-react/CheckLineIcon'
import { SwarmButton } from '../../components/SwarmButton'
import { SwarmTextInput } from '../../components/SwarmTextInput'
import { Context as SettingsContext } from '../../providers/Settings'
import { Context as StampsContext } from '../../providers/Stamps'
import { ROUTES } from '../../routes'
import { calculateStampPrice, convertAmountToSeconds, secondsToTimeString, waitUntilStampExists } from '../../utils'
interface Props {
onFinished: () => void
}
const useStyles = makeStyles((theme: Theme) =>
createStyles({
link: {
color: '#dd7700',
textDecoration: 'underline',
'&:hover': {
textDecoration: 'none',
// https://github.com/mui-org/material-ui/issues/22543
'@media (hover: none)': {
textDecoration: 'none',
},
},
},
buttonSelected: {
color: 'white',
backgroundColor: theme.palette.primary.main,
},
}),
)
const marks = [
{ value: 1, label: '1 day' },
{ value: 365, label: '365 days' },
]
export function PostageStampStandardCreation({ onFinished }: Props): ReactElement {
const classes = useStyles()
const { refresh } = useContext(StampsContext)
const { beeDebugApi } = useContext(SettingsContext)
const [depthInput, setDepthInput] = useState<number>(Utils.getDepthForCapacity(4))
const [amountInput, setAmountInput] = useState<string>(Utils.getAmountForTtl(30))
const [labelInput, setLabelInput] = useState('')
const [submitting, setSubmitting] = useState(false)
const [buttonValue, setButtonValue] = useState(4)
function sliderValueChange(_: unknown, newValue: number | number[]) {
if (typeof newValue !== 'number') {
return
}
const amountValue = Utils.getAmountForTtl(newValue)
setAmountInput(amountValue)
}
const { enqueueSnackbar } = useSnackbar()
function getTtl(amount: string): string {
const pricePerBlock = 24000
return `${secondsToTimeString(
convertAmountToSeconds(parseInt(amount, 10), pricePerBlock),
)} (with price of ${pricePerBlock.toFixed(0)} per block)`
}
function getPrice(depth: number, amount: bigint): string {
const price = calculateStampPrice(depth, amount)
return `${price.toSignificantDigits()} xBZZ`
}
async function submit() {
try {
// This is really just a typeguard, the validation pretty much guarantees these will have the right values
if (!depthInput || !amountInput) {
return
}
if (!beeDebugApi) {
return
}
setSubmitting(true)
const amount = BigInt(amountInput)
const depth = depthInput
const options: PostageBatchOptions = {
waitForUsable: false,
label: labelInput || undefined,
immutableFlag: true,
}
const batchId = await beeDebugApi.createPostageBatch(amount.toString(), depth, options)
await waitUntilStampExists(batchId, beeDebugApi)
await refresh()
onFinished()
} catch (e) {
console.error(e) // eslint-disable-line
enqueueSnackbar(`Error: ${(e as Error).message}`, { variant: 'error' })
}
setSubmitting(false)
}
function handleBatchSize(gigabytes: number) {
setButtonValue(gigabytes)
const capacity = Utils.getDepthForCapacity(gigabytes)
setDepthInput(capacity)
}
return (
<>
<Box mb={4}>
<Typography>
A postage stamp batch containes postage stamps that will give you the right to upload data to the Swarm
network. If you&apos;re not familiar with this, please read&nbsp;
<a
href="https://medium.com/ethereum-swarm/how-to-upload-data-to-the-swarm-network-c0766c3ae381"
target="_blank"
rel="noreferrer"
>
this guide
</a>
.
</Typography>
</Box>
<Box mb={1}>
<Typography variant="h2">Batch name</Typography>
</Box>
<Box mb={2}>
<SwarmTextInput name="depth" label="Label" onChange={e => setLabelInput(e.target.value)} />
</Box>
<Box mb={1}>
<Typography variant="h2">Batch size</Typography>
</Box>
<Box mb={2}>
<Grid container justifyContent="space-between" spacing={2}>
<Grid item xs={4}>
<Button
variant="contained"
fullWidth
onClick={() => handleBatchSize(4)}
className={buttonValue === 4 ? classes.buttonSelected : ''}
>
4 GB
</Button>
</Grid>
<Grid item xs={4}>
<Button
variant="contained"
fullWidth
onClick={() => handleBatchSize(32)}
className={buttonValue === 32 ? classes.buttonSelected : ''}
>
32 GB
</Button>
</Grid>
<Grid item xs={4}>
<Button
variant="contained"
fullWidth
onClick={() => handleBatchSize(256)}
className={buttonValue === 256 ? classes.buttonSelected : ''}
>
256 GB
</Button>
</Grid>
</Grid>
</Box>
<Box mb={1}>
<Typography variant="h2">Data persistence</Typography>
</Box>
<Box mb={2}>
<Slider
aria-label="Volume"
min={1}
max={365}
step={1}
marks={marks}
valueLabelDisplay="auto"
defaultValue={30}
onChange={sliderValueChange}
/>
</Box>
<Box mb={2}>
<Box mt={0.25} sx={{ bgcolor: '#f6f6f6' }} p={2}>
<Grid container justifyContent="space-between">
<Typography>Corresponding TTL (Time to live)</Typography>
<Typography>{amountInput ? getTtl(amountInput) : '-'}</Typography>
</Grid>
</Box>
<Box display="flex" justifyContent={'right'} mt={0.5}>
<Typography style={{ fontSize: '10px', color: 'rgba(0, 0, 0, 0.26)' }}>
Current price of 24000 per block
</Typography>
</Box>
</Box>
<Box mb={4} sx={{ bgcolor: '#fcf2e8' }} p={2}>
<Grid container justifyContent="space-between">
<Typography>Indicative Price</Typography>
<Typography>{getPrice(depthInput, BigInt(amountInput))}</Typography>
</Grid>
</Box>
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<SwarmButton
disabled={submitting || !depthInput || !amountInput}
onClick={submit}
iconType={Check}
loading={submitting}
>
Buy New Stamp
</SwarmButton>
</Grid>
<Grid item>
<Link to={ROUTES.ACCOUNT_STAMPS_NEW_ADVANCED} className={classes.link}>
Advanced mode
</Link>
</Grid>
</Grid>
</>
)
}
+10 -3
View File
@@ -1,5 +1,6 @@
import { ReactElement, useContext } from 'react'
import TimerFlash from 'remixicon-react/TimerFlashFillIcon'
import TimerFlashFill from 'remixicon-react/TimerFlashFillIcon'
import TimerFlashLine from 'remixicon-react/TimerFlashLineIcon'
import ExpandableElement from '../../components/ExpandableElement'
import ExpandableList from '../../components/ExpandableList'
import ExpandableListItem from '../../components/ExpandableListItem'
@@ -50,8 +51,14 @@ function StampsTable({ postageStamps }: Props): ReactElement | null {
<ExpandableListItem label="Purchase Block Number" value={stamp.blockNumber} />
<ExpandableListItemActions>
<StampExtensionModal
label="Topup & Dilute"
icon={<TimerFlash size="1rem" />}
type="Topup"
icon={<TimerFlashFill size="1rem" />}
beeDebug={beeDebugApi}
stamp={stamp.batchID}
/>
<StampExtensionModal
type="Dilute"
icon={<TimerFlashLine size="1rem" />}
beeDebug={beeDebugApi}
stamp={stamp.batchID}
/>
+1 -1
View File
@@ -44,7 +44,7 @@ export default function Stamp(): ReactElement {
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
function navigateToNewStamp() {
navigate(ROUTES.ACCOUNT_STAMPS_NEW)
navigate(ROUTES.ACCOUNT_STAMPS_NEW_STANDARD)
}
return (
+6 -3
View File
@@ -16,7 +16,7 @@ import GiftCards from './pages/gift-code'
import Info from './pages/info'
import LightModeRestart from './pages/restart/LightModeRestart'
import Settings from './pages/settings'
import { CreatePostageStampPage } from './pages/stamps/CreatePostageStampPage'
import { CreatePostageStampPage } from './pages/stamps/CreatePostageStampAdvancedPage'
import Status from './pages/status'
import TopUp from './pages/top-up'
import { BankCardTopUpIndex } from './pages/top-up/BankCardTopUpIndex'
@@ -25,6 +25,7 @@ import { GiftCardFund } from './pages/top-up/GiftCardFund'
import { GiftCardTopUpIndex } from './pages/top-up/GiftCardTopUpIndex'
import { Swap } from './pages/top-up/Swap'
import { Context as SettingsContext } from './providers/Settings'
import { CreatePostageStampBasicPage } from './pages/stamps/CreatePostageStampStandardPage'
export enum ROUTES {
INFO = '/',
@@ -46,7 +47,8 @@ export enum ROUTES {
ACCOUNT_WALLET = '/account/wallet',
ACCOUNT_CHEQUEBOOK = '/account/chequebook',
ACCOUNT_STAMPS = '/account/stamps',
ACCOUNT_STAMPS_NEW = '/account/stamps/new',
ACCOUNT_STAMPS_NEW_STANDARD = '/account/stamps/new',
ACCOUNT_STAMPS_NEW_ADVANCED = '/account/stamps/new/advanced',
ACCOUNT_FEEDS = '/account/feeds',
ACCOUNT_FEEDS_NEW = '/account/feeds/new',
ACCOUNT_FEEDS_UPDATE = '/account/feeds/update/:hash',
@@ -86,7 +88,8 @@ const BaseRouter = (): ReactElement => {
<Route path={ROUTES.ACCOUNT_WALLET} element={<AccountWallet />} />
<Route path={ROUTES.ACCOUNT_CHEQUEBOOK} element={<AccountChequebook />} />
<Route path={ROUTES.ACCOUNT_STAMPS} element={<AccountStamps />} />
<Route path={ROUTES.ACCOUNT_STAMPS_NEW} element={<CreatePostageStampPage />} />
<Route path={ROUTES.ACCOUNT_STAMPS_NEW_STANDARD} element={<CreatePostageStampBasicPage />} />
<Route path={ROUTES.ACCOUNT_STAMPS_NEW_ADVANCED} element={<CreatePostageStampPage />} />
<Route path={ROUTES.ACCOUNT_FEEDS} element={<AccountFeeds />} />
<Route path={ROUTES.ACCOUNT_FEEDS_NEW} element={<CreateNewFeed />} />
<Route path={ROUTES.ACCOUNT_FEEDS_UPDATE} element={<UpdateFeed />} />
+17
View File
@@ -159,6 +159,23 @@ const componentsOverrides = (theme: Theme) => ({
backgroundColor: 'transparent',
},
},
MuiSlider: {
root: {
'& .MuiSlider-valueLabel': {
top: '-27px',
'& span': {
height: '20px',
borderRadius: '0px',
transform: 'none',
'& span': {
display: 'flex',
alignItems: 'center',
transform: 'none',
},
},
},
},
},
})
const propsOverrides = {