feat: sync and update with all changes from solar-punk-ltd fork (#730)

* fix: swap error caused by invalid id and batchcount
* fix: enhance creation messages for admin drive and user drives
* fix: identity and wallet creation
* fix: asset preview types
* fix: fm search unicode text
* fix: feed identity and stamp usage
* fix: ui display changes
* fix: stamp buy and dilute
* fix: vite polyfill warning for stream
* fix: standard mode postage stamp purchase reserves incorrect size and duration
* fix: add syncing message for Bee node and update page state handling
* refactor: stamp depth and amount validation

---------

Co-authored-by: Balint Ujvari <balint.ujvari@solarpunk.buzz>
Co-authored-by: Bálint Ujvári <58116288+bosi95@users.noreply.github.com>
Co-authored-by: rolandlor <33499567+rolandlor@users.noreply.github.com>
This commit is contained in:
Ferenc Sárai
2026-04-02 14:53:20 +02:00
committed by GitHub
parent 4848b5be97
commit cb5adfe031
41 changed files with 627 additions and 380 deletions
+1 -1
View File
@@ -40,7 +40,7 @@ export default function ExpandableListItem({ label, value, tooltip }: Props): Re
)}
{value && (
<Box flex={1} textAlign="right">
<Typography variant="body2">
<Typography variant="body2" component="div">
{value}
{tooltip && (
<Tooltip title={tooltip} placement="top" arrow>
+68 -60
View File
@@ -16,11 +16,17 @@ const useStyles = makeStyles()(theme => ({
header: {
backgroundColor: theme.palette.background.paper,
marginBottom: theme.spacing(0.25),
borderLeft: `${theme.spacing(0.25)}px solid rgba(0,0,0,0)`,
borderLeft: `${theme.spacing(0.25)} solid rgba(0,0,0,0)`,
wordBreak: 'break-word',
'&:hover': {
backgroundColor: theme.palette.background.paper,
},
'&:focus-within': {
backgroundColor: theme.palette.background.paper,
},
},
headerOpen: {
borderLeft: `${theme.spacing(0.25)}px solid ${theme.palette.primary.main}`,
borderLeft: `${theme.spacing(0.25)} solid ${theme.palette.primary.main}`,
},
copyValue: {
cursor: 'pointer',
@@ -95,35 +101,35 @@ export default function ExpandableListItemInput({
}
return (
<ListItemButton className={`${classes.header} ${open ? classes.headerOpen : ''}`}>
<Box display="flex" flexDirection="column" width="100%">
<Box display="flex" flexDirection="row" alignItems="center" width="100%">
{label && (
<Box flex={1} minWidth={0}>
<Typography variant="body1" className={classes.unselectableLabel} component="span">
{label}
</Typography>
<>
<ListItemButton className={`${classes.header} ${open ? classes.headerOpen : ''}`}>
<Box display="flex" flexDirection="column" width="100%">
<Box display="flex" flexDirection="row" alignItems="center" width="100%">
{label && (
<Box flex={1} minWidth={0}>
<Typography variant="body1" className={classes.unselectableLabel} component="span">
{label}
</Typography>
</Box>
)}
<Box flex={3} display="flex" alignItems="center" justifyContent="flex-end" minWidth={0} gap={1}>
{!open && value && (
<Typography
variant="body2"
component="span"
sx={{ minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
>
{value}
</Typography>
)}
{!expandedOnly && !locked && (
<IconButton size="small" className={classes.copyValue} onClick={toggleOpen}>
{open ? <Minus strokeWidth={1} /> : <Edit strokeWidth={1} />}
</IconButton>
)}
</Box>
)}
<Box flex={3} display="flex" alignItems="center" justifyContent="flex-end" minWidth={0} gap={1}>
{!open && value && (
<Typography
variant="body2"
component="span"
sx={{ minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
>
{value}
</Typography>
)}
{!expandedOnly && !locked && (
<IconButton size="small" className={classes.copyValue} onClick={toggleOpen}>
{open ? <Minus strokeWidth={1} /> : <Edit strokeWidth={1} />}
</IconButton>
)}
</Box>
</Box>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box display="flex" flexDirection="column" width="100%">
<Collapse in={open} timeout="auto" unmountOnExit>
<Box display="flex" alignItems="center" width="100%" minWidth={0}>
<InputBase
value={inputValue}
@@ -146,36 +152,38 @@ export default function ExpandableListItemInput({
/>
</Box>
{helperText && <ExpandableListItemNote>{helperText}</ExpandableListItemNote>}
<Box mt={2}>
<ExpandableListItemActions>
<SwarmButton
disabled={
loading ||
inputValue === value ||
Boolean(confirmLabelDisabled) ||
(inputValue === '' && value === undefined)
}
loading={loading}
iconType={confirmIcon ?? Check}
onClick={() => {
onConfirm?.(inputValue.trim())
}}
>
{confirmLabel || 'Save'}
</SwarmButton>
<SwarmButton
disabled={loading || inputValue === value || inputValue === ''}
iconType={X}
onClick={() => setInputValue(value || '')}
cancel
>
Cancel
</SwarmButton>
</ExpandableListItemActions>
</Box>
</Box>
</Collapse>
</Box>
</ListItemButton>
</Collapse>
</Box>
</ListItemButton>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box mt={2}>
<ExpandableListItemActions>
<SwarmButton
disabled={
loading ||
inputValue === value ||
Boolean(confirmLabelDisabled) ||
(inputValue === '' && value === undefined)
}
loading={loading}
iconType={confirmIcon ?? Check}
onClick={() => {
onConfirm?.(inputValue.trim())
}}
>
{confirmLabel || 'Save'}
</SwarmButton>
<SwarmButton
disabled={loading || inputValue === value || inputValue === ''}
iconType={X}
onClick={() => setInputValue(value || '')}
cancel
>
Cancel
</SwarmButton>
</ExpandableListItemActions>
</Box>
</Collapse>
</>
)
}
+9 -3
View File
@@ -10,11 +10,17 @@ const useStyles = makeStyles()(theme => ({
header: {
backgroundColor: theme.palette.background.paper,
marginBottom: theme.spacing(0.25),
borderLeft: `${theme.spacing(0.25)}px solid rgba(0,0,0,0)`,
borderLeft: `${theme.spacing(0.25)} solid rgba(0,0,0,0)`,
wordBreak: 'break-word',
'&:hover': {
backgroundColor: theme.palette.background.paper,
},
'&:focus-within': {
backgroundColor: theme.palette.background.paper,
},
},
headerOpen: {
borderLeft: `${theme.spacing(0.25)}px solid ${theme.palette.primary.main}`,
borderLeft: `${theme.spacing(0.25)} solid ${theme.palette.primary.main}`,
},
copyValue: {
cursor: 'pointer',
@@ -69,7 +75,7 @@ export default function ExpandableListItemKey({ label, value, expanded }: Props)
return (
<ListItemButton className={`${classes.header} ${open ? classes.headerOpen : ''}`}>
<Grid container direction="column" justifyContent="space-between" alignItems="stretch">
<Grid container direction="column" justifyContent="space-between" alignItems="stretch" style={{ width: '100%' }}>
<Grid container direction="row" justifyContent="space-between" alignItems="center">
{label && (
<Typography variant="body1" component="span">
+8 -2
View File
@@ -10,11 +10,17 @@ const useStyles = makeStyles()(theme => ({
header: {
backgroundColor: theme.palette.background.paper,
marginBottom: theme.spacing(0.25),
borderLeft: `${theme.spacing(0.25)}px solid rgba(0,0,0,0)`,
borderLeft: `${theme.spacing(0.25)} solid rgba(0,0,0,0)`,
wordBreak: 'break-word',
'&:hover': {
backgroundColor: theme.palette.background.paper,
},
'&:focus-within': {
backgroundColor: theme.palette.background.paper,
},
},
headerOpen: {
borderLeft: `${theme.spacing(0.25)}px solid ${theme.palette.primary.main}`,
borderLeft: `${theme.spacing(0.25)} solid ${theme.palette.primary.main}`,
},
openLinkIcon: {
cursor: 'pointer',
+1 -1
View File
@@ -139,7 +139,7 @@ export default function SideBar(): ReactElement {
label: 'File Manager',
path: ROUTES.FILEMANAGER,
icon: FileManagerIcon,
pathMatcherSubstring: '/filemanager/',
pathMatcherSubstring: '/filemanager',
},
{
label: 'Account',
+95 -17
View File
@@ -1,4 +1,4 @@
import { BatchId, Bee } from '@ethersphere/bee-js'
import { Bee, PostageBatch } from '@ethersphere/bee-js'
import { Box } from '@mui/material'
import Button from '@mui/material/Button'
import Dialog from '@mui/material/Dialog'
@@ -8,19 +8,48 @@ import DialogTitle from '@mui/material/DialogTitle'
import Input from '@mui/material/Input'
import { useSnackbar } from 'notistack'
import React, { ReactElement, ReactNode, useState } from 'react'
import { makeStyles } from 'tss-react/mui'
interface Props {
type: 'Topup' | 'Dilute'
icon: ReactNode
bee: Bee
stamp: BatchId
import { CheckState } from '../providers/Bee'
const useStyles = makeStyles()(theme => ({
buttonSelected: {
color: 'white',
backgroundColor: theme.palette.primary.main,
'&:hover': {
color: theme.palette.secondary.main,
backgroundColor: 'white',
'@media (hover: none)': {
color: 'white',
backgroundColor: theme.palette.primary.main,
},
},
},
buttonUnselected: {
color: '#dd7700',
backgroundColor: 'white',
},
}))
export enum StampExtensionType {
Topup = 'Topup',
Dilute = 'Dilute',
}
export default function StampExtensionModal({ type, icon, bee, stamp }: Props): ReactElement {
const [open, setOpen] = useState(false)
const [amount, setAmount] = useState('')
interface Props {
type: StampExtensionType
icon: ReactNode
bee: Bee
stamp: PostageBatch
status: CheckState
}
export default function StampExtensionModal({ type, icon, bee, stamp, status }: Props): ReactElement {
const { classes } = useStyles()
const [open, setOpen] = useState<boolean>(false)
const [amount, setAmount] = useState<string>('')
const { enqueueSnackbar } = useSnackbar()
const label = `${type} ${stamp.toHex().substring(0, 8)}`
const label = `${type} ${stamp.batchID.toHex().substring(0, 8)}`
const handleClickOpen = (e: React.MouseEvent<HTMLButtonElement>) => {
setOpen(true)
@@ -32,23 +61,65 @@ export default function StampExtensionModal({ type, icon, bee, stamp }: Props):
}
const handleAction = async () => {
if (type === 'Topup') {
if (status !== CheckState.OK) {
enqueueSnackbar(`Node connection status is not ${CheckState.OK}: ${status}`, { variant: 'error' })
return
}
if (type === StampExtensionType.Topup) {
const isAmountInvalid = BigInt(amount) <= BigInt(0)
if (isAmountInvalid) {
enqueueSnackbar(`Invalid amount: ${amount}, it must be greate than 0`, { variant: 'error' })
return
}
try {
await bee.topUpBatch(stamp, amount)
await bee.topUpBatch(stamp.batchID, amount)
enqueueSnackbar(`Successfully topped up stamp, your changes will appear soon`, { variant: 'success' })
} catch (error) {
enqueueSnackbar(`Failed to topup stamp: ${error || 'Unknown reason'}`, { variant: 'error' })
}
return
}
if (type === 'Dilute') {
if (type === StampExtensionType.Dilute) {
const newDepth = parseInt(amount, 10)
const ttlDays = stamp.duration.toDays()
const currentDepth = stamp.depth
const maxHalvings = Math.floor(Math.log2(ttlDays)) + currentDepth
const isDepthInvalid = newDepth > maxHalvings || newDepth <= currentDepth
if (isDepthInvalid) {
enqueueSnackbar(`Invalid depth: ${newDepth} (${currentDepth} < new depth < ${maxHalvings})`, {
variant: 'error',
})
return
}
if (ttlDays <= 2) {
enqueueSnackbar(`TTL: ${ttlDays} <= 2 days, cannot dilute stamp (min. TTL is 1 day)`, {
variant: 'warning',
})
return
}
try {
await bee.diluteBatch(stamp, parseInt(amount, 10))
await bee.diluteBatch(stamp.batchID, newDepth)
enqueueSnackbar(`Successfully diluted stamp, your changes will appear soon`, { variant: 'success' })
} catch (error) {
enqueueSnackbar(`Failed to dilute stamp: ${error || 'Unknown reason'}`, { variant: 'error' })
}
return
}
enqueueSnackbar(`Failed to extend stamp, unknown operation: ${type}`, { variant: 'error' })
}
const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
@@ -57,7 +128,7 @@ export default function StampExtensionModal({ type, icon, bee, stamp }: Props):
return (
<Box mb={2}>
<Button variant="contained" onClick={handleClickOpen} startIcon={icon}>
<Button className={classes.buttonSelected} variant="contained" onClick={handleClickOpen} startIcon={icon}>
{type}
</Button>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
@@ -68,7 +139,7 @@ export default function StampExtensionModal({ type, icon, bee, stamp }: Props):
margin="dense"
id="name"
type="text"
placeholder={type === 'Topup' ? 'Amount to add' : 'New depth to dilute'}
placeholder={type === StampExtensionType.Topup ? 'Amount to add' : 'New depth to dilute'}
fullWidth
value={amount}
onChange={handleChange}
@@ -78,7 +149,14 @@ export default function StampExtensionModal({ type, icon, bee, stamp }: Props):
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button disabled={amount === ''} onClick={handleAction} color="primary">
<Button
disabled={amount === ''}
onClick={async () => {
await handleAction()
handleClose()
}}
color="primary"
>
{type}
</Button>
</DialogActions>
+1
View File
@@ -75,6 +75,7 @@ export function SwarmSelect({
value={value}
className={classes.select}
displayEmpty
onChange={onChange}
renderValue={(value: unknown) => (value ? renderValue(value) : placeholder)}
MenuProps={{ MenuListProps: { disablePadding: true }, PaperProps: { square: true } }}
>
+2 -2
View File
@@ -57,7 +57,7 @@ export function SwarmTextInput({
variant="filled"
className={classes.field}
defaultValue={defaultValue || ''}
InputProps={{ disableUnderline: true }}
slotProps={{ input: { disableUnderline: true } }}
placeholder={placeholder}
/>
)
@@ -73,7 +73,7 @@ export function SwarmTextInput({
className={classes.field}
defaultValue={defaultValue || ''}
onChange={onChange}
InputProps={{ disableUnderline: true }}
slotProps={{ input: { disableUnderline: true } }}
placeholder={placeholder}
/>
)
+6 -6
View File
@@ -24,14 +24,14 @@ const useStyles = makeStyles()(theme => ({
},
},
buttonSelected: {
color: 'white',
backgroundColor: theme.palette.primary.main,
color: theme.palette.secondary.main,
backgroundColor: 'white',
'&:hover': {
color: theme.palette.secondary.main,
backgroundColor: 'white',
color: 'white',
backgroundColor: theme.palette.primary.main,
'@media (hover: none)': {
color: 'white',
backgroundColor: theme.palette.primary.main,
color: theme.palette.secondary.main,
backgroundColor: 'white',
},
},
},