diff --git a/src/components/PeerDetail.tsx b/src/components/PeerDetail.tsx
index afa415c..a685399 100644
--- a/src/components/PeerDetail.tsx
+++ b/src/components/PeerDetail.tsx
@@ -7,9 +7,10 @@ function truncStringPortion(str: string, firstCharCount = 10, endCharCount = 10)
interface Props {
peerId: string
+ characterLength?: number
}
-export default function PeerDetail(props: Props): ReactElement {
+export default function PeerDetail({ peerId, characterLength }: Props): ReactElement {
return (
- {truncStringPortion(props.peerId)}
+ {truncStringPortion(peerId, characterLength, characterLength)}
)
}
diff --git a/src/components/TabsContainer.tsx b/src/components/TabsContainer.tsx
new file mode 100644
index 0000000..0100f1e
--- /dev/null
+++ b/src/components/TabsContainer.tsx
@@ -0,0 +1,71 @@
+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'
+
+interface TabPanelProps {
+ children?: ReactNode
+ index: number
+ value: number
+}
+
+function TabPanel(props: TabPanelProps) {
+ const { children, value, index, ...other } = props
+
+ return (
+
+ {value === index && (
+
+ {children}
+
+ )}
+
+ )
+}
+
+const useStyles = makeStyles(() => ({
+ root: {
+ flexGrow: 1,
+ },
+}))
+
+interface TabsValues {
+ component: ReactNode
+ label: string
+}
+
+interface Props {
+ values: TabsValues[]
+}
+
+export default function SimpleTabs({ values }: Props): ReactElement {
+ const classes = useStyles()
+ const [value, setValue] = React.useState(0)
+
+ const handleChange = (event: React.ChangeEvent>, newValue: number) => {
+ setValue(newValue)
+ }
+
+ return (
+
+
+ {values.map(({ label }, index) => (
+
+ ))}
+
+ {values.map(({ component }, index) => (
+
+ {component}
+
+ ))}
+
+ )
+}
diff --git a/src/pages/files/Download.tsx b/src/pages/files/Download.tsx
new file mode 100644
index 0000000..5641e12
--- /dev/null
+++ b/src/pages/files/Download.tsx
@@ -0,0 +1,65 @@
+import { ReactElement, useState } 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 { apiHost } from '../../constants'
+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 [referenceInput, setReferenceInput] = useState('')
+ const [referenceError, setReferenceError] = useState(null)
+
+ const handleReferenceChange = (e: React.ChangeEvent) => {
+ setReferenceInput(e.target.value)
+
+ if (Utils.Hex.isHexString(e.target.value, 64) || Utils.Hex.isHexString(e.target.value, 128)) setReferenceError(null)
+ else setReferenceError(new Error('Incorrect format of swarm hash'))
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+ {referenceError && {referenceError.message}}
+ >
+ )
+}
diff --git a/src/pages/files/SelectStamp.tsx b/src/pages/files/SelectStamp.tsx
new file mode 100644
index 0000000..7449343
--- /dev/null
+++ b/src/pages/files/SelectStamp.tsx
@@ -0,0 +1,48 @@
+import React, { ReactElement } from 'react'
+import Button from '@material-ui/core/Button'
+import Menu from '@material-ui/core/Menu'
+import MenuItem from '@material-ui/core/MenuItem'
+import ListItemIcon from '@material-ui/core/ListItemIcon'
+import { PostageBatch } from '@ethersphere/bee-js'
+import PeerDetailDrawer from '../../components/PeerDetail'
+
+interface Props {
+ stamps: PostageBatch[] | null
+ selectedStamp: PostageBatch | null
+ setSelected: (stamp: PostageBatch) => void
+}
+
+export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props): ReactElement | null {
+ const [anchorEl, setAnchorEl] = React.useState(null)
+
+ if (!stamps) return null
+
+ const handleClick = (event: React.MouseEvent) => {
+ setAnchorEl(event.currentTarget)
+ }
+
+ const handleClose = () => setAnchorEl(null)
+
+ return (
+
+
+
+
+ )
+}
diff --git a/src/pages/files/Upload.tsx b/src/pages/files/Upload.tsx
new file mode 100644
index 0000000..4349197
--- /dev/null
+++ b/src/pages/files/Upload.tsx
@@ -0,0 +1,101 @@
+import { ReactElement, useContext, useEffect, useState } from 'react'
+import { beeApi } from '../../services/bee'
+
+import { Button, Container, CircularProgress, FormHelperText } from '@material-ui/core'
+import { DropzoneArea } from 'material-ui-dropzone'
+import ClipboardCopy from '../../components/ClipboardCopy'
+import { PostageBatch } from '@ethersphere/bee-js'
+import { Context } from '../../providers/Stamps'
+import PeerDetailDrawer from '../../components/PeerDetail'
+import Chip from '@material-ui/core/Chip'
+import Avatar from '@material-ui/core/Avatar'
+import SelectStamp from './SelectStamp'
+import CreatePostageStamp from '../stamps/CreatePostageStampModal'
+
+export default function Files(): ReactElement {
+ const [file, setFile] = useState(null)
+ const [uploadReference, setUploadReference] = useState('')
+ const [uploadError, setUploadError] = useState(null)
+ const [isUploadingFile, setIsUploadingFile] = useState(false)
+
+ const [selectedStamp, setSelectedStamp] = useState(null)
+
+ const { isLoading, error, stamps } = useContext(Context)
+
+ // Choose a postage stamp that has the lowest utilization
+ useEffect(() => {
+ if (!selectedStamp && stamps && stamps.length > 0) {
+ const stamp = stamps.reduce((prev, curr) => {
+ if (curr.utilization < prev.utilization) return curr
+
+ return prev
+ }, stamps[0])
+
+ setSelectedStamp(stamp)
+ }
+ }, [isLoading, error, stamps, selectedStamp])
+
+ const uploadFile = () => {
+ if (file === null || selectedStamp === null) return
+ setIsUploadingFile(true)
+ setUploadError(null)
+ beeApi.files
+ .uploadFile(selectedStamp.batchID, file)
+ .then(hash => {
+ setUploadReference(hash)
+ setFile(null)
+ })
+ .catch(setUploadError) // FIXME: should instead trigger notification
+ .finally(() => {
+ setIsUploadingFile(false)
+ })
+ }
+
+ const handleChange = (files?: File[]) => {
+ if (files) {
+ setFile(files[0])
+ setUploadReference('')
+ }
+ }
+
+ return (
+
+
+
+
+ {selectedStamp && (
+
+
+ with Postage Stamp{' '}
+ {selectedStamp.utilization}}
+ label={}
+ deleteIcon={}
+ onDelete={() => {} /* eslint-disable-line*/}
+ variant="outlined"
+ />
+
+
+
+ )}
+ {!selectedStamp &&
}
+
+ {isUploadingFile && (
+
+
+
+ )}
+ {uploadReference && (
+
+ {uploadReference}
+
+
+ )}
+ {uploadError &&
{uploadError.message}}
+
+
+
+ )
+}
diff --git a/src/pages/files/index.tsx b/src/pages/files/index.tsx
index 6f421ab..1f16a10 100644
--- a/src/pages/files/index.tsx
+++ b/src/pages/files/index.tsx
@@ -1,58 +1,17 @@
-import { ReactElement, useState } from 'react'
+import { ReactElement } from 'react'
-import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
-import {
- Paper,
- InputBase,
- IconButton,
- Typography,
- Container,
- CircularProgress,
- FormHelperText,
-} from '@material-ui/core'
-import { Search } from '@material-ui/icons'
+import { Container, CircularProgress } from '@material-ui/core'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { useApiHealth, useDebugApiHealth } from '../../hooks/apiHooks'
-import { apiHost } from '../../constants'
-import { Utils } from '@ethersphere/bee-js'
-
-const useStyles = makeStyles((theme: Theme) =>
- createStyles({
- root: {
- padding: '2px 4px',
- display: 'flex',
- alignItems: 'center',
- },
- input: {
- marginLeft: theme.spacing(1),
- flex: 1,
- },
- iconButton: {
- padding: 10,
- },
- divider: {
- height: 28,
- margin: 4,
- },
- }),
-)
+import Download from './Download'
+import Upload from './Upload'
+import TabsContainer from '../../components/TabsContainer'
export default function Files(): ReactElement {
- const classes = useStyles()
-
- const [referenceInput, setReferenceInput] = useState('')
- const [referenceError, setReferenceError] = useState(null)
const { health, isLoadingHealth } = useApiHealth()
const { nodeHealth, isLoadingNodeHealth } = useDebugApiHealth()
- const handleReferenceChange = (e: React.ChangeEvent) => {
- setReferenceInput(e.target.value)
-
- if (Utils.Hex.isHexString(e.target.value, 64)) setReferenceError(null)
- else setReferenceError(new Error('Incorrect format of swarm hash'))
- }
-
if (isLoadingHealth || isLoadingNodeHealth) {
return (
@@ -65,30 +24,18 @@ export default function Files(): ReactElement {
return (
-
-
- Download
-
-
-
-
-
-
-
-
- {referenceError && {referenceError.message}}
+ ,
+ },
+ {
+ label: 'upload',
+ component: ,
+ },
+ ]}
+ />
)
}
diff --git a/src/theme.tsx b/src/theme.tsx
index 133f713..5d11889 100644
--- a/src/theme.tsx
+++ b/src/theme.tsx
@@ -1,4 +1,5 @@
-import { createMuiTheme } from '@material-ui/core/styles'
+import { createMuiTheme, Theme } from '@material-ui/core/styles'
+import { orange } from '@material-ui/core/colors'
declare module '@material-ui/core/styles/createPalette' {
interface TypeBackground {
@@ -6,6 +7,54 @@ declare module '@material-ui/core/styles/createPalette' {
}
}
+// Overwriting default components styles
+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(','),
+ '&:hover': {
+ color: theme.palette.secondary,
+ opacity: 1,
+ },
+ '&$selected': {
+ color: theme.palette.secondary,
+ fontWeight: theme.typography.fontWeightMedium,
+ },
+ '&:focus': {
+ color: theme.palette.secondary,
+ },
+ },
+ },
+ MuiTabs: {
+ root: {
+ borderBottom: 'none',
+ },
+ indicator: {
+ backgroundColor: theme.palette.primary.main,
+ },
+ },
+})
+
+const propsOverrides = {
+ MuiTab: {
+ disableRipple: true,
+ },
+}
+
export const lightTheme = createMuiTheme({
palette: {
type: 'light',
@@ -13,7 +62,9 @@ export const lightTheme = createMuiTheme({
default: '#fafafa',
},
primary: {
- main: '#6a6a6a',
+ light: orange.A200,
+ main: '#dd7700',
+ dark: orange[800],
},
secondary: {
main: '#333333',
@@ -32,7 +83,9 @@ export const darkTheme = createMuiTheme({
paper: '#161b22',
},
primary: {
+ light: orange.A200,
main: '#dd7700',
+ dark: orange[800],
},
secondary: {
main: '#1f2937',
@@ -42,3 +95,8 @@ export const darkTheme = createMuiTheme({
fontFamily: ['Work Sans', 'Montserrat', 'Nunito', 'Roboto', '"Helvetica Neue"', 'Arial', 'sans-serif'].join(','),
},
})
+
+darkTheme.overrides = componentsOverrides(darkTheme)
+darkTheme.props = propsOverrides
+lightTheme.overrides = componentsOverrides(lightTheme)
+lightTheme.props = propsOverrides