feat: files page updated to latest design (#218)
* feat: altered the design of the tabs and redid the download tab * feat: redesign the upload file * fix: styles of tabs on hover * fix: display troubleshoot component when the status of the node is not OK * fix: when removing the file, remove the reference upload reference as well * fix: on inputs the label should not be selectable * feat: add placeholder to inputs and make the label non-selectable * refactor: improved the readability of the upload file component * chore: removed PeerDetail component * fix: replaced "batch" with (postage) "stamp" for clarity * refactor: address PR review comments * feat: disable the download button if there is no value
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import { ReactElement, useState } from 'react'
|
||||
import { ReactElement, ChangeEvent, useState } from 'react'
|
||||
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
|
||||
import Collapse from '@material-ui/core/Collapse'
|
||||
import { ListItem, Typography, Grid, IconButton, InputBase, Button } from '@material-ui/core'
|
||||
import { Edit, Minus, RotateCcw, Check } from 'react-feather'
|
||||
|
||||
import ExpandableListItemActions from './ExpandableListItemActions'
|
||||
import ExpandableListItemNote from './ExpandableListItemNote'
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -33,44 +34,80 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
keyMargin: {
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
unselectableLabel: {
|
||||
cursor: 'default',
|
||||
userSelect: 'none',
|
||||
// Many browsers don't support yet the general user-select css property
|
||||
WebkitUserSelect: 'none',
|
||||
MozUserSelect: 'none',
|
||||
msUserSelect: 'none',
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
interface Props {
|
||||
label: string
|
||||
value: string
|
||||
value?: string
|
||||
placeholder?: string
|
||||
helperText?: string
|
||||
expandedOnly?: boolean
|
||||
confirmLabel?: string
|
||||
confirmLabelDisabled?: boolean
|
||||
onChange?: (value: string) => void
|
||||
onConfirm: (value: string) => void
|
||||
}
|
||||
|
||||
export default function ExpandableListItemKey({ label, value, onConfirm }: Props): ReactElement | null {
|
||||
export default function ExpandableListItemKey({
|
||||
label,
|
||||
value,
|
||||
onConfirm,
|
||||
onChange,
|
||||
confirmLabel,
|
||||
confirmLabelDisabled,
|
||||
expandedOnly,
|
||||
helperText,
|
||||
placeholder,
|
||||
}: Props): ReactElement | null {
|
||||
const classes = useStyles()
|
||||
const [open, setOpen] = useState(false)
|
||||
const [inputValue, setInputValue] = useState(value)
|
||||
const [open, setOpen] = useState(Boolean(expandedOnly))
|
||||
const [inputValue, setInputValue] = useState<string>(value || '')
|
||||
const toggleOpen = () => setOpen(!open)
|
||||
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setInputValue(e.target.value)
|
||||
|
||||
if (onChange) onChange(e.target.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ListItem className={`${classes.header} ${open ? classes.headerOpen : ''}`}>
|
||||
<Grid container direction="column" justifyContent="space-between" alignItems="stretch">
|
||||
<Grid container direction="row" justifyContent="space-between" alignItems="center">
|
||||
{label && <Typography variant="body1">{label}</Typography>}
|
||||
{label && (
|
||||
<Typography variant="body1" className={classes.unselectableLabel}>
|
||||
{label}
|
||||
</Typography>
|
||||
)}
|
||||
<Typography variant="body2">
|
||||
<div>
|
||||
{!open && value}
|
||||
<IconButton size="small" className={classes.copyValue}>
|
||||
{open ? (
|
||||
<Minus onClick={toggleOpen} strokeWidth={1} />
|
||||
) : (
|
||||
<Edit onClick={toggleOpen} strokeWidth={1} />
|
||||
)}
|
||||
</IconButton>
|
||||
{!expandedOnly && (
|
||||
<IconButton size="small" className={classes.copyValue}>
|
||||
{open ? (
|
||||
<Minus onClick={toggleOpen} strokeWidth={1} />
|
||||
) : (
|
||||
<Edit onClick={toggleOpen} strokeWidth={1} />
|
||||
)}
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<InputBase
|
||||
value={inputValue}
|
||||
onChange={e => setInputValue(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
onChange={handleChange}
|
||||
fullWidth
|
||||
className={classes.content}
|
||||
autoFocus
|
||||
@@ -79,20 +116,25 @@ export default function ExpandableListItemKey({ label, value, onConfirm }: Props
|
||||
</Grid>
|
||||
</ListItem>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
{helperText && <ExpandableListItemNote>{helperText}</ExpandableListItemNote>}
|
||||
<ExpandableListItemActions>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={inputValue === value}
|
||||
disabled={
|
||||
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
|
||||
}
|
||||
startIcon={<Check size="1rem" />}
|
||||
onClick={() => onConfirm(inputValue)}
|
||||
>
|
||||
Save
|
||||
{confirmLabel || 'Save'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={inputValue === value}
|
||||
disabled={inputValue === value || inputValue === ''}
|
||||
startIcon={<RotateCcw size="1rem" />}
|
||||
onClick={() => setInputValue(value)}
|
||||
onClick={() => setInputValue(value || '')}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { ReactElement } from 'react'
|
||||
import { Typography } from '@material-ui/core'
|
||||
|
||||
function truncStringPortion(str: string, firstCharCount = 10, endCharCount = 10) {
|
||||
return `${str.substring(0, firstCharCount)}...${str.substring(str.length - endCharCount, str.length)}`
|
||||
}
|
||||
|
||||
interface Props {
|
||||
peerId: string
|
||||
characterLength?: number
|
||||
}
|
||||
|
||||
export default function PeerDetail({ peerId, characterLength }: Props): ReactElement {
|
||||
return (
|
||||
<Typography
|
||||
variant="button"
|
||||
style={{
|
||||
fontFamily: 'monospace, monospace',
|
||||
}}
|
||||
>
|
||||
{truncStringPortion(peerId, characterLength, characterLength)}
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
import React, { ReactElement, ReactNode } from 'react'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import Tabs from '@material-ui/core/Tabs'
|
||||
import Tab from '@material-ui/core/Tab'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import Box from '@material-ui/core/Box'
|
||||
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
|
||||
import { Tab, Tabs } from '@material-ui/core'
|
||||
|
||||
interface TabPanelProps {
|
||||
children?: ReactNode
|
||||
@@ -16,24 +13,25 @@ function TabPanel(props: TabPanelProps) {
|
||||
|
||||
return (
|
||||
<div role="tabpanel" hidden={value !== index} {...other}>
|
||||
{value === index && (
|
||||
<Box p={3}>
|
||||
<Typography>{children}</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{value === index && children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
}))
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
content: {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
interface TabsValues {
|
||||
component: ReactNode
|
||||
label: string
|
||||
label: ReactNode
|
||||
}
|
||||
|
||||
interface Props {
|
||||
@@ -55,16 +53,18 @@ export default function SimpleTabs({ values, index, indexChanged }: Props): Reac
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Tabs value={v} onChange={handleChange}>
|
||||
<Tabs value={v} onChange={handleChange} variant="fullWidth">
|
||||
{values.map(({ label }, idx) => (
|
||||
<Tab key={idx} label={label} />
|
||||
))}
|
||||
</Tabs>
|
||||
{values.map(({ component }, idx) => (
|
||||
<TabPanel key={idx} value={v} index={idx}>
|
||||
{component}
|
||||
</TabPanel>
|
||||
))}
|
||||
<div className={classes.content}>
|
||||
{values.map(({ component }, idx) => (
|
||||
<TabPanel key={idx} value={v} index={idx}>
|
||||
{component}
|
||||
</TabPanel>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,66 +1,28 @@
|
||||
import { ReactElement, useState, useContext } from 'react'
|
||||
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
|
||||
import { Paper, InputBase, IconButton, FormHelperText } from '@material-ui/core'
|
||||
import { Search } from '@material-ui/icons'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import ExpandableListItemInput from '../../components/ExpandableListItemInput'
|
||||
import { Utils } from '@ethersphere/bee-js'
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
padding: theme.spacing(0.25),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
input: {
|
||||
marginLeft: theme.spacing(1),
|
||||
flex: 1,
|
||||
},
|
||||
iconButton: {
|
||||
padding: 10,
|
||||
},
|
||||
divider: {
|
||||
height: 28,
|
||||
margin: 4,
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
export default function Files(): ReactElement {
|
||||
const classes = useStyles()
|
||||
const { apiUrl } = useContext(SettingsContext)
|
||||
|
||||
const [referenceInput, setReferenceInput] = useState('')
|
||||
const [referenceError, setReferenceError] = useState<Error | null>(null)
|
||||
const [referenceError, setReferenceError] = useState<string | undefined>(undefined)
|
||||
|
||||
const handleReferenceChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
setReferenceInput(e.target.value)
|
||||
|
||||
if (Utils.isHexString(e.target.value, 64) || Utils.isHexString(e.target.value, 128)) setReferenceError(null)
|
||||
else setReferenceError(new Error('Incorrect format of swarm hash'))
|
||||
const validateChange = (value: string) => {
|
||||
if (Utils.isHexString(value, 64) || Utils.isHexString(value, 128)) setReferenceError(undefined)
|
||||
else setReferenceError('Incorrect format of swarm hash. Expected 64 or 128 hexstring characters.')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Paper className={classes.root}>
|
||||
<InputBase
|
||||
className={classes.input}
|
||||
placeholder="Enter swarm reference e.g. 0773a91efd6547c754fc1d95fb1c62c7d1b47f959c2caa685dfec8736da95c1c"
|
||||
inputProps={{ 'aria-label': 'retrieve file from swarm' }}
|
||||
value={referenceInput}
|
||||
onChange={handleReferenceChange}
|
||||
/>
|
||||
<IconButton
|
||||
href={`${apiUrl}/bzz/${referenceInput}`}
|
||||
target="_blank"
|
||||
disabled={referenceError !== null || !referenceInput}
|
||||
className={classes.iconButton}
|
||||
aria-label="download"
|
||||
>
|
||||
<Search />
|
||||
</IconButton>
|
||||
</Paper>
|
||||
{referenceError && <FormHelperText error>{referenceError.message}</FormHelperText>}
|
||||
</>
|
||||
<ExpandableListItemInput
|
||||
label="Swarm Hash"
|
||||
onConfirm={value => window.open(`${apiUrl}/bzz/${value}`, '_blank')}
|
||||
onChange={validateChange}
|
||||
helperText={referenceError}
|
||||
confirmLabel={'Download'}
|
||||
confirmLabelDisabled={Boolean(referenceError)}
|
||||
placeholder="e.g. 31fb0362b1a42536134c86bc58b97ac0244e5c6630beec3e27c2d1cecb38c605"
|
||||
expandedOnly
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import Button from '@material-ui/core/Button'
|
||||
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
||||
import Menu from '@material-ui/core/Menu'
|
||||
import MenuItem from '@material-ui/core/MenuItem'
|
||||
import React, { ReactElement } from 'react'
|
||||
import PeerDetailDrawer from '../../components/PeerDetail'
|
||||
import { Button, ListItemIcon, Typography, Menu, MenuItem } from '@material-ui/core'
|
||||
import { EnrichedPostageBatch } from '../../providers/Stamps'
|
||||
|
||||
interface Props {
|
||||
@@ -25,10 +21,10 @@ export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button aria-controls="simple-menu" aria-haspopup="true" onClick={handleClick}>
|
||||
<Button variant="contained" aria-haspopup="true" onClick={handleClick}>
|
||||
Change
|
||||
</Button>
|
||||
<Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
|
||||
<Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
|
||||
{stamps.map(stamp => (
|
||||
<MenuItem
|
||||
key={stamp.batchID}
|
||||
@@ -39,7 +35,7 @@ export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props
|
||||
selected={stamp.batchID === selectedStamp?.batchID}
|
||||
>
|
||||
<ListItemIcon>{stamp.usageText}</ListItemIcon>
|
||||
<PeerDetailDrawer peerId={stamp.batchID} />
|
||||
<Typography variant="body2">{stamp.batchID.substr(0, 8)}[…]</Typography>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
|
||||
+106
-57
@@ -1,20 +1,31 @@
|
||||
import { Button, CircularProgress, Container } from '@material-ui/core'
|
||||
import Avatar from '@material-ui/core/Avatar'
|
||||
import Chip from '@material-ui/core/Chip'
|
||||
import { Button, CircularProgress, Container, Avatar, Chip, Typography } from '@material-ui/core'
|
||||
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
|
||||
import { DropzoneArea } from 'material-ui-dropzone'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { RotateCcw, Check } from 'react-feather'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import UploadSizeAlert from '../../components/AlertUploadSize'
|
||||
import ClipboardCopy from '../../components/ClipboardCopy'
|
||||
import PeerDetailDrawer from '../../components/PeerDetail'
|
||||
import { Context, EnrichedPostageBatch } from '../../providers/Stamps'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import CreatePostageStamp from '../stamps/CreatePostageStampModal'
|
||||
import SelectStamp from './SelectStamp'
|
||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
import ExpandableListItemNote from '../../components/ExpandableListItemNote'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
content: { marginTop: theme.spacing(2) },
|
||||
loadingProgress: { textAlign: 'center', padding: '50px' },
|
||||
}),
|
||||
)
|
||||
|
||||
const MAX_FILE_SIZE = 1_000_000_000 // 1 gigabyte
|
||||
|
||||
export default function Files(): ReactElement {
|
||||
const classes = useStyles()
|
||||
const [dropzoneKey, setDropzoneKey] = useState(0)
|
||||
const [file, setFile] = useState<File | null>(null)
|
||||
const [uploadReference, setUploadReference] = useState('')
|
||||
@@ -22,10 +33,14 @@ export default function Files(): ReactElement {
|
||||
|
||||
const [selectedStamp, setSelectedStamp] = useState<EnrichedPostageBatch | null>(null)
|
||||
|
||||
const { isLoading, error, stamps } = useContext(Context)
|
||||
const { isLoading, error, stamps, refresh } = useContext(Context)
|
||||
const { beeApi } = useContext(SettingsContext)
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
useEffect(() => {
|
||||
refresh()
|
||||
}, [])
|
||||
|
||||
// Choose a postage stamp that has the lowest usage
|
||||
useEffect(() => {
|
||||
if (!selectedStamp && stamps && stamps.length > 0) {
|
||||
@@ -47,68 +62,102 @@ export default function Files(): ReactElement {
|
||||
setIsUploadingFile(true)
|
||||
beeApi
|
||||
.uploadFile(selectedStamp.batchID, file)
|
||||
.then(hash => {
|
||||
window.setTimeout(() => {
|
||||
setFile(null)
|
||||
setUploadReference(hash.reference)
|
||||
setDropzoneKey(dropzoneKey + 1)
|
||||
}, 0)
|
||||
})
|
||||
.then(hash => setUploadReference(hash.reference))
|
||||
.catch(e => enqueueSnackbar(`Error uploading: ${e.message}`, { variant: 'error' }))
|
||||
.finally(() => {
|
||||
setIsUploadingFile(false)
|
||||
})
|
||||
.finally(() => setIsUploadingFile(false))
|
||||
}
|
||||
|
||||
const uploadNew = () => {
|
||||
setTimeout(() => {
|
||||
setFile(null)
|
||||
setDropzoneKey(dropzoneKey + 1)
|
||||
setUploadReference('')
|
||||
}, 0)
|
||||
}
|
||||
|
||||
const handleChange = (files?: File[]) => {
|
||||
setUploadReference('')
|
||||
|
||||
if (files) {
|
||||
setFile(files[0])
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<DropzoneArea
|
||||
key={'dropzone-' + dropzoneKey}
|
||||
onChange={handleChange}
|
||||
filesLimit={1}
|
||||
maxFileSize={MAX_FILE_SIZE}
|
||||
/>
|
||||
<div style={{ marginTop: '15px' }}>
|
||||
{selectedStamp && (
|
||||
<div style={{ display: 'flex' }}>
|
||||
<small>
|
||||
with Postage Stamp{' '}
|
||||
<Chip
|
||||
avatar={<Avatar>{selectedStamp.usageText}</Avatar>}
|
||||
label={<PeerDetailDrawer peerId={selectedStamp.batchID} characterLength={6} />}
|
||||
deleteIcon={<ClipboardCopy value={selectedStamp.batchID} />}
|
||||
onDelete={() => {} /* eslint-disable-line*/}
|
||||
variant="outlined"
|
||||
/>
|
||||
</small>
|
||||
<SelectStamp stamps={stamps} selectedStamp={selectedStamp} setSelected={setSelectedStamp} />
|
||||
</div>
|
||||
)}
|
||||
{!selectedStamp && <CreatePostageStamp />}
|
||||
<Button disabled={!file && isUploadingFile && !selectedStamp} onClick={() => uploadFile()}>
|
||||
Upload
|
||||
</Button>
|
||||
{file && <UploadSizeAlert file={file} />}
|
||||
{isUploadingFile && (
|
||||
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||
<CircularProgress />
|
||||
</Container>
|
||||
)}
|
||||
{uploadReference && (
|
||||
<div style={{ marginBottom: '15px', display: 'flex' }}>
|
||||
<span>{uploadReference}</span>
|
||||
<ClipboardCopy value={uploadReference} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<>
|
||||
<DropzoneArea
|
||||
key={'dropzone-' + dropzoneKey}
|
||||
onChange={handleChange}
|
||||
filesLimit={1}
|
||||
maxFileSize={MAX_FILE_SIZE}
|
||||
/>
|
||||
<div className={classes.content}>
|
||||
{/* We have file and can upload display stamp selection */}
|
||||
{file && !isUploadingFile && !uploadReference && (
|
||||
<>
|
||||
<ExpandableListItemNote>
|
||||
To upload this file to your node, you need a postage stamp. You can buy a new one or you can use an
|
||||
existing stamp (providing it’s sufficient for this file).
|
||||
</ExpandableListItemNote>
|
||||
{selectedStamp && (
|
||||
<ExpandableListItem
|
||||
label={
|
||||
<>
|
||||
Upload with Postage Stamp{' '}
|
||||
<Chip
|
||||
avatar={<Avatar>{selectedStamp.usageText}</Avatar>}
|
||||
label={<Typography variant="body2">{selectedStamp.batchID.substr(0, 8)}[…]</Typography>}
|
||||
deleteIcon={<ClipboardCopy value={selectedStamp.batchID} />}
|
||||
onDelete={() => {} /* eslint-disable-line*/}
|
||||
variant="outlined"
|
||||
/>
|
||||
</>
|
||||
}
|
||||
value={<SelectStamp stamps={stamps} selectedStamp={selectedStamp} setSelected={setSelectedStamp} />}
|
||||
/>
|
||||
)}
|
||||
{!selectedStamp && (
|
||||
<ExpandableListItemActions>
|
||||
<CreatePostageStamp />
|
||||
</ExpandableListItemActions>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* We have file and can upload display upload button */}
|
||||
{file && !uploadReference && (
|
||||
<>
|
||||
<ExpandableListItemActions>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={!file && isUploadingFile && !selectedStamp}
|
||||
onClick={() => uploadFile()}
|
||||
startIcon={<Check size="1rem" />}
|
||||
>
|
||||
Upload
|
||||
</Button>
|
||||
{isUploadingFile && (
|
||||
<Container className={classes.loadingProgress}>
|
||||
<CircularProgress />
|
||||
</Container>
|
||||
)}
|
||||
</ExpandableListItemActions>
|
||||
<UploadSizeAlert file={file} />
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* File has already been uploaded */}
|
||||
{uploadReference && (
|
||||
<>
|
||||
<ExpandableListItemKey label="Swarm Reference" value={uploadReference} />
|
||||
<ExpandableListItemActions>
|
||||
<Button variant="contained" onClick={uploadNew} startIcon={<RotateCcw size="1rem" />}>
|
||||
Upload New File
|
||||
</Button>
|
||||
</ExpandableListItemActions>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
+15
-19
@@ -1,32 +1,28 @@
|
||||
import { ReactElement, useContext } from 'react'
|
||||
|
||||
import { Container } from '@material-ui/core'
|
||||
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { Context } from '../../providers/Bee'
|
||||
import Download from './Download'
|
||||
import Upload from './Upload'
|
||||
import TabsContainer from '../../components/TabsContainer'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
|
||||
export default function Files(): ReactElement {
|
||||
const { status } = useContext(Context)
|
||||
const { status } = useContext(BeeContext)
|
||||
|
||||
if (!status.all) return <TroubleshootConnectionCard />
|
||||
|
||||
return (
|
||||
<Container maxWidth="sm">
|
||||
<TabsContainer
|
||||
values={[
|
||||
{
|
||||
label: 'download',
|
||||
component: <Download />,
|
||||
},
|
||||
{
|
||||
label: 'upload',
|
||||
component: <Upload />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Container>
|
||||
<TabsContainer
|
||||
values={[
|
||||
{
|
||||
label: 'download',
|
||||
component: <Download />,
|
||||
},
|
||||
{
|
||||
label: 'upload',
|
||||
component: <Upload />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import { Container, CircularProgress } from '@material-ui/core'
|
||||
import StampsTable from './StampsTable'
|
||||
import CreatePostageStampModal from './CreatePostageStampModal'
|
||||
|
||||
import { Context } from '../../providers/Stamps'
|
||||
import { Context as StampsContext } from '../../providers/Stamps'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
@@ -25,7 +27,11 @@ const useStyles = makeStyles(() =>
|
||||
|
||||
export default function Accounting(): ReactElement {
|
||||
const classes = useStyles()
|
||||
const { stamps, isLoading, error, start, stop } = useContext(Context)
|
||||
const { stamps, isLoading, error, start, stop } = useContext(StampsContext)
|
||||
const { status } = useContext(BeeContext)
|
||||
|
||||
if (!status.all) return <TroubleshootConnectionCard />
|
||||
|
||||
useEffect(() => {
|
||||
start()
|
||||
|
||||
|
||||
+7
-21
@@ -68,32 +68,18 @@ const componentsOverrides = (theme: Theme) => ({
|
||||
},
|
||||
MuiTab: {
|
||||
root: {
|
||||
backgroundColor: 'transparent',
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
marginRight: theme.spacing(4),
|
||||
fontFamily: [
|
||||
'-apple-system',
|
||||
'BlinkMacSystemFont',
|
||||
'"Segoe UI"',
|
||||
'Roboto',
|
||||
'"Helvetica Neue"',
|
||||
'Arial',
|
||||
'sans-serif',
|
||||
'"Apple Color Emoji"',
|
||||
'"Segoe UI Emoji"',
|
||||
'"Segoe UI Symbol"',
|
||||
].join(','),
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
'&:hover': {
|
||||
color: theme.palette.secondary,
|
||||
backgroundColor: '#fcf2e8',
|
||||
color: theme.palette.primary.main,
|
||||
opacity: 1,
|
||||
},
|
||||
'&$selected': {
|
||||
color: theme.palette.secondary,
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
},
|
||||
'&:focus': {
|
||||
color: theme.palette.secondary,
|
||||
},
|
||||
},
|
||||
textColorInherit: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
MuiTabs: {
|
||||
@@ -101,7 +87,7 @@ const componentsOverrides = (theme: Theme) => ({
|
||||
borderBottom: 'none',
|
||||
},
|
||||
indicator: {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user