Skip to content

Commit

Permalink
feat: add website and folder upload and download (#260)
Browse files Browse the repository at this point in the history
* feat: add website and folder upload and download

* feat: download-share-upload navigation

* fix: check for files length in hasIndexDocument

* fix: change router dependency

* refactor: switch to @ethersphere/manfest-js

* fix: hide previews on dropzone, fix spinner align, hide 0 size display

* feat: add upload and download history

* refactor: change drag and drop text

* feat: make history ux better

* refactor: improve code based on review

* build: add missing react-router dependency

* ci: remove beeload

* revert(ci): remove beeload

This reverts commit 4ce6cb0.
  • Loading branch information
Cafe137 committed Dec 7, 2021
1 parent dec812b commit 3ef1ad9
Show file tree
Hide file tree
Showing 26 changed files with 838 additions and 268 deletions.
217 changes: 194 additions & 23 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@
},
"dependencies": {
"@ethersphere/bee-js": "3.0.0",
"@ethersphere/manifest-js": "^1.0.0",
"@material-ui/core": "4.12.3",
"@material-ui/icons": "4.11.2",
"@material-ui/lab": "4.0.0-alpha.57",
"axios": "0.24.0",
"bignumber.js": "9.0.1",
"file-saver": "^2.0.5",
"formik": "2.2.9",
"formik-material-ui": "3.0.1",
"jszip": "^3.7.1",
"material-ui-dropzone": "3.5.0",
"notistack": "1.0.10",
"opener": "1.5.2",
Expand All @@ -41,6 +44,7 @@
"react-dom": "17.0.2",
"react-feather": "2.0.9",
"react-identicons": "1.2.5",
"react-router": "5.2.0",
"react-router-dom": "5.2.0",
"react-syntax-highlighter": "15.4.4",
"semver": "7.3.5",
Expand All @@ -50,6 +54,7 @@
"@commitlint/config-conventional": "14.1.0",
"@testing-library/jest-dom": "5.15.0",
"@testing-library/react": "12.1.2",
"@types/file-saver": "^2.0.4",
"@types/jest": "27.0.2",
"@types/qrcode.react": "1.0.2",
"@types/react": "17.0.34",
Expand Down
43 changes: 22 additions & 21 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
import CssBaseline from '@material-ui/core/CssBaseline'
import { ThemeProvider } from '@material-ui/core/styles'
import { SnackbarProvider } from 'notistack'
import { ReactElement } from 'react'
import { BrowserRouter as Router } from 'react-router-dom'
import './App.css'

import { ThemeProvider } from '@material-ui/core/styles'
import CssBaseline from '@material-ui/core/CssBaseline'
import { SnackbarProvider } from 'notistack'

import BaseRouter from './routes'
import Dashboard from './layout/Dashboard'
import { theme } from './theme'
import { Provider as StampsProvider } from './providers/Stamps'
import { Provider as PlatformProvider } from './providers/Platform'
import { Provider as BeeProvider } from './providers/Bee'
import { Provider as FileProvider } from './providers/File'
import { Provider as PlatformProvider } from './providers/Platform'
import { Provider as SettingsProvider } from './providers/Settings'
import { Provider as StampsProvider } from './providers/Stamps'
import BaseRouter from './routes'
import { theme } from './theme'

const App = (): ReactElement => (
<div className="App">
<ThemeProvider theme={theme}>
<SettingsProvider>
<BeeProvider>
<StampsProvider>
<PlatformProvider>
<SnackbarProvider>
<Router>
<>
<CssBaseline />
<Dashboard>
<BaseRouter />
</Dashboard>
</>
</Router>
</SnackbarProvider>
</PlatformProvider>
<FileProvider>
<PlatformProvider>
<SnackbarProvider>
<Router>
<>
<CssBaseline />
<Dashboard>
<BaseRouter />
</Dashboard>
</>
</Router>
</SnackbarProvider>
</PlatformProvider>
</FileProvider>
</StampsProvider>
</BeeProvider>
</SettingsProvider>
Expand Down
45 changes: 35 additions & 10 deletions src/components/ExpandableListItemLink.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Grid, IconButton, ListItem, Tooltip, Typography } from '@material-ui/core'
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
import { OpenInNewSharp } from '@material-ui/icons'
import { ArrowForward, OpenInNewSharp } from '@material-ui/icons'
import { ReactElement, useState } from 'react'
import CopyToClipboard from 'react-copy-to-clipboard'
import { useHistory } from 'react-router'

const useStyles = makeStyles((theme: Theme) =>
createStyles({
Expand Down Expand Up @@ -46,31 +47,55 @@ const useStyles = makeStyles((theme: Theme) =>
interface Props {
label: string
value: string
link?: string
navigationType?: 'NEW_WINDOW' | 'HISTORY_PUSH'
allowClipboard?: boolean
}

export default function ExpandableListItemLink({ label, value }: Props): ReactElement | null {
export default function ExpandableListItemLink({
label,
value,
link,
navigationType = 'NEW_WINDOW',
allowClipboard = true,
}: Props): ReactElement | null {
const classes = useStyles()
const [copied, setCopied] = useState(false)
const history = useHistory()

const tooltipClickHandler = () => setCopied(true)
const tooltipCloseHandler = () => setCopied(false)

const displayValue = value.length > 22 ? value.slice(0, 19) + '...' : value

function onNavigation() {
if (navigationType === 'NEW_WINDOW') {
window.open(link || value)
} else {
history.push(link || value)
}
}

return (
<ListItem className={classes.header}>
<Grid container direction="column" justifyContent="space-between" alignItems="stretch">
<Grid container direction="row" justifyContent="space-between" alignItems="center">
{label && <Typography variant="body1">{label}</Typography>}
<Typography variant="body2">
<div>
<span className={classes.copyValue}>
<Tooltip title={copied ? 'Copied' : 'Copy'} placement="top" arrow onClose={tooltipCloseHandler}>
<CopyToClipboard text={value}>
<span onClick={tooltipClickHandler}>{value.slice(0, 19)}...</span>
</CopyToClipboard>
</Tooltip>
</span>
{allowClipboard && (
<span className={classes.copyValue}>
<Tooltip title={copied ? 'Copied' : 'Copy'} placement="top" arrow onClose={tooltipCloseHandler}>
<CopyToClipboard text={value}>
<span onClick={tooltipClickHandler}>{displayValue}</span>
</CopyToClipboard>
</Tooltip>
</span>
)}
{!allowClipboard && <span onClick={onNavigation}>{displayValue}</span>}
<IconButton size="small" className={classes.openLinkIcon}>
<OpenInNewSharp onClick={() => window.open(value)} strokeWidth={1} />
{navigationType === 'NEW_WINDOW' && <OpenInNewSharp onClick={onNavigation} strokeWidth={1} />}
{navigationType === 'HISTORY_PUSH' && <ArrowForward onClick={onNavigation} strokeWidth={1} />}
</IconButton>
</div>
</Typography>
Expand Down
37 changes: 37 additions & 0 deletions src/components/History.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ReactElement, useEffect, useState } from 'react'
import { getPrettyDateString } from '../utils/date'
import { getHistorySafe, HistoryItem, HISTORY_KEYS } from '../utils/local-storage'
import ExpandableList from './ExpandableList'
import ExpandableListItemLink from './ExpandableListItemLink'

interface Props {
title: string
localStorageKey: HISTORY_KEYS
}

export function History({ title, localStorageKey }: Props): ReactElement | null {
const [items, setItems] = useState<HistoryItem[]>([])

useEffect(() => {
setItems(getHistorySafe(localStorageKey))
}, [localStorageKey])

if (!items.length) {
return null
}

return (
<ExpandableList label={title} defaultOpen>
{items.map((x, i) => (
<ExpandableListItemLink
label={getPrettyDateString(new Date(x.createdAt))}
value={x.name}
link={'/files/hash/' + x.hash}
key={i}
navigationType="HISTORY_PUSH"
allowClipboard={false}
/>
))}
</ExpandableList>
)
}
41 changes: 41 additions & 0 deletions src/components/HistoryHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
import { ArrowBack } from '@material-ui/icons'
import { ReactElement } from 'react'
import { useHistory } from 'react-router-dom'

interface Props {
children: string
}

const useStyles = makeStyles(() =>
createStyles({
pressable: {
cursor: 'pointer',
},
icon: {
color: '#242424',
},
}),
)

export function HistoryHeader({ children }: Props): ReactElement {
const classes = useStyles()
const history = useHistory()

function goBack() {
history.goBack()
}

return (
<Box mb={4}>
<Grid container direction="row">
<Box mr={2}>
<div className={classes.pressable} onClick={goBack}>
<ArrowBack className={classes.icon} />
</div>
</Box>
<Typography variant="h1">{children}</Typography>
</Grid>
</Box>
)
}
10 changes: 10 additions & 0 deletions src/components/Loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { CircularProgress, Grid } from '@material-ui/core'
import { ReactElement } from 'react'

export function Loading(): ReactElement {
return (
<Grid container direction="row" justifyContent="center" alignItems="center">
<CircularProgress />
</Grid>
)
}
14 changes: 6 additions & 8 deletions src/components/SideBar.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { Divider, Drawer, Grid, Link as MUILink, List } from '@material-ui/core'
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
import { OpenInNewSharp } from '@material-ui/icons'
import type { ReactElement } from 'react'
import { BookOpen, DollarSign, FileText, Home, Layers, Settings } from 'react-feather'
import { Link } from 'react-router-dom'

import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'
import { OpenInNewSharp } from '@material-ui/icons'
import { Divider, List, Drawer, Grid, Link as MUILink } from '@material-ui/core'
import { Home, FileText, DollarSign, Settings, Layers, BookOpen } from 'react-feather'
import Logo from '../assets/logo.svg'
import { ROUTES } from '../routes'
import SideBarItem from './SideBarItem'
import SideBarStatus from './SideBarStatus'

import Logo from '../assets/logo.svg'

const navBarItems = [
{
label: 'Info',
Expand All @@ -19,7 +17,7 @@ const navBarItems = [
},
{
label: 'Files',
path: ROUTES.FILES,
path: ROUTES.UPLOAD,
icon: FileText,
},
{
Expand Down
17 changes: 12 additions & 5 deletions src/pages/files/AssetPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import { Web } from '@material-ui/icons'
import { ReactElement, useEffect, useState } from 'react'
import { File, Folder } from 'react-feather'
import { FitImage } from '../../components/FitImage'
import { detectIndexHtml, getHumanReadableFileSize } from '../../utils/file'
import { detectIndexHtml, getAssetNameFromFiles, getHumanReadableFileSize } from '../../utils/file'
import { SwarmFile } from '../../utils/SwarmFile'
import { AssetIcon } from './AssetIcon'

interface Props {
assetName?: string
files: SwarmFile[]
}

export function AssetPreview({ files }: Props): ReactElement {
// TODO: add optional prop for indexDocument when it is already known (e.g. downloading a manifest)

export function AssetPreview({ assetName, files }: Props): ReactElement {
const [previewComponent, setPreviewComponent] = useState<ReactElement | undefined>(undefined)
const [previewUri, setPreviewUri] = useState<string | undefined>(undefined)

Expand Down Expand Up @@ -39,11 +42,13 @@ export function AssetPreview({ files }: Props): ReactElement {
}, [files])

const getPrimaryText = () => {
const name = getAssetNameFromFiles(files)

if (files.length === 1) {
return 'Filename: ' + files[0].name
return 'Filename: ' + (assetName || name)
}

return 'Folder name: ' + files[0].path.split('/')[0]
return 'Folder name: ' + (assetName || name)
}

const getKind = () => {
Expand All @@ -66,6 +71,8 @@ export function AssetPreview({ files }: Props): ReactElement {
return getHumanReadableFileSize(bytes)
}

const size = getSize()

return (
<Box mb={4}>
<Box bgcolor="background.paper">
Expand All @@ -78,7 +85,7 @@ export function AssetPreview({ files }: Props): ReactElement {
<Box p={2}>
<Typography>{getPrimaryText()}</Typography>
<Typography>Kind: {getKind()}</Typography>
<Typography>Size: {getSize()}</Typography>
{size !== '0 bytes' && <Typography>Size: {size}</Typography>}
</Box>
</Grid>
</Box>
Expand Down
24 changes: 24 additions & 0 deletions src/pages/files/AssetSummary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Box, Typography } from '@material-ui/core'
import { ReactElement } from 'react'
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
import ExpandableListItemLink from '../../components/ExpandableListItemLink'

interface Props {
hash: string
}

export function AssetSummary({ hash }: Props): ReactElement {
return (
<>
<Box mb={4}>
<ExpandableListItemKey label="Swarm hash" value={hash} />
<ExpandableListItemLink label="Share on Swarm Gateway" value={`https://gateway.ethswarm.org/access/${hash}`} />
</Box>
<Typography>
The Swarm Gateway is graciously provided by the Swarm Foundation. This service is under development and provided
for testing purposes only. Learn more at{' '}
<a href="https://gateway.ethswarm.org/">https://gateway.ethswarm.org/</a>.
</Typography>
</>
)
}
Loading

0 comments on commit 3ef1ad9

Please sign in to comment.