feat: sync and update with all changes from fork (#720)
* feat: sync and update with all changes from fork * refactor: extract clipboard copy logic into custom hook * fix: correct spelling of DEFAULT_REFRESH_FREQUENCY_MS in Stamps and WalletBalance providers * refactor(ui-tests): replace fixed sleeps with condition-based waits * fix: handle null values for size and granteeCount in infoGroups * fix(lint): add newline at end of file in useClipboardCopy hook * fix(ui-tests): page.goto URL * refactor: update import paths for useClipboardCopy --------- Co-authored-by: Ferenc Sárai <sarai.ferenc@gmail.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { createStyles, makeStyles, Tab, Tabs, Theme } from '@material-ui/core'
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import { Tab, Tabs } from '@mui/material'
|
||||
import React, { ReactElement, useContext } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { makeStyles } from 'tss-react/mui'
|
||||
|
||||
import { Context } from '../../providers/Bee'
|
||||
import { ACCOUNT_TABS } from '../../routes'
|
||||
|
||||
@@ -17,28 +19,26 @@ interface Props {
|
||||
active: 'WALLET' | 'CHEQUEBOOK' | 'STAMPS' | 'FEEDS' | 'STAKING'
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
marginBottom: theme.spacing(4),
|
||||
textTransform: 'none',
|
||||
marginLeft: theme.spacing(-0.25),
|
||||
marginRight: theme.spacing(-0.25),
|
||||
},
|
||||
tab: {
|
||||
marginLeft: theme.spacing(0.25),
|
||||
marginRight: theme.spacing(0.25),
|
||||
},
|
||||
}),
|
||||
)
|
||||
const useStyles = makeStyles()(theme => ({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
marginBottom: theme.spacing(4),
|
||||
textTransform: 'none',
|
||||
marginLeft: theme.spacing(-0.25),
|
||||
marginRight: theme.spacing(-0.25),
|
||||
},
|
||||
tab: {
|
||||
marginLeft: theme.spacing(0.25),
|
||||
marginRight: theme.spacing(0.25),
|
||||
},
|
||||
}))
|
||||
|
||||
export function AccountNavigation({ active }: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
const { classes } = useStyles()
|
||||
const navigate = useNavigate()
|
||||
const { nodeInfo } = useContext(Context)
|
||||
|
||||
function onChange(event: React.ChangeEvent<Record<string, never>>, newValue: number) {
|
||||
function onChange(_event: React.SyntheticEvent, newValue: number) {
|
||||
navigate(ACCOUNT_TABS[newValue])
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
export function Header(): ReactElement {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Box } from '@material-ui/core'
|
||||
import { Box } from '@mui/material'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
|
||||
import ExpandableList from '../../../components/ExpandableList'
|
||||
import ExpandableListItem from '../../../components/ExpandableListItem'
|
||||
import ExpandableListItemActions from '../../../components/ExpandableListItemActions'
|
||||
@@ -8,7 +9,7 @@ import TroubleshootConnectionCard from '../../../components/TroubleshootConnecti
|
||||
import DepositModal from '../../../containers/DepositModal'
|
||||
import WithdrawModal from '../../../containers/WithdrawModal'
|
||||
import { useAccounting } from '../../../hooks/accounting'
|
||||
import { Context as BeeContext, CheckState } from '../../../providers/Bee'
|
||||
import { CheckState, Context as BeeContext } from '../../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../../providers/Settings'
|
||||
import PeerBalances from '../../accounting/PeerBalances'
|
||||
import { AccountNavigation } from '../AccountNavigation'
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { Box } from '@material-ui/core'
|
||||
import { NULL_TOPIC } from '@ethersphere/bee-js'
|
||||
import { Box } from '@mui/material'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
|
||||
import Trash from 'remixicon-react/DeleteBin7LineIcon'
|
||||
import Download from 'remixicon-react/Download2LineIcon'
|
||||
import Info from 'remixicon-react/InformationLineIcon'
|
||||
|
||||
import ExpandableList from '../../../components/ExpandableList'
|
||||
import ExpandableListItem from '../../../components/ExpandableListItem'
|
||||
import ExpandableListItemActions from '../../../components/ExpandableListItemActions'
|
||||
import ExpandableListItemKey from '../../../components/ExpandableListItemKey'
|
||||
import { SwarmButton } from '../../../components/SwarmButton'
|
||||
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext, CheckState } from '../../../providers/Bee'
|
||||
import { Identity, Context as IdentityContext } from '../../../providers/Feeds'
|
||||
import { CheckState, Context as BeeContext } from '../../../providers/Bee'
|
||||
import { Context as IdentityContext, Identity } from '../../../providers/Feeds'
|
||||
import { ROUTES } from '../../../routes'
|
||||
import { formatEnum } from '../../../utils'
|
||||
import { persistIdentitiesWithoutUpdate } from '../../../utils/identity'
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
|
||||
import ExpandableList from '../../../components/ExpandableList'
|
||||
import ExpandableListItem from '../../../components/ExpandableListItem'
|
||||
import ExpandableListItemActions from '../../../components/ExpandableListItemActions'
|
||||
import { Loading } from '../../../components/Loading'
|
||||
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
|
||||
import StakeModal from '../../../containers/StakeModal'
|
||||
import { Context as BeeContext, CheckState } from '../../../providers/Bee'
|
||||
import { CheckState, Context as BeeContext } from '../../../providers/Bee'
|
||||
import { AccountNavigation } from '../AccountNavigation'
|
||||
import { Header } from '../Header'
|
||||
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
import { CircularProgress, Container, createStyles, makeStyles } from '@material-ui/core'
|
||||
import { CircularProgress, Container } from '@mui/material'
|
||||
import { ReactElement, useContext, useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
|
||||
import { makeStyles } from 'tss-react/mui'
|
||||
|
||||
import { ChainSync } from '../../../components/ChainSync'
|
||||
import { Loading } from '../../../components/Loading'
|
||||
import { SwarmButton } from '../../../components/SwarmButton'
|
||||
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext, CheckState } from '../../../providers/Bee'
|
||||
import { CheckState, Context as BeeContext } from '../../../providers/Bee'
|
||||
import { Context as StampsContext } from '../../../providers/Stamps'
|
||||
import { ROUTES } from '../../../routes'
|
||||
import StampsTable from '../../stamps/StampsTable'
|
||||
import { AccountNavigation } from '../AccountNavigation'
|
||||
import { Header } from '../Header'
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
root: {
|
||||
width: '100%',
|
||||
display: 'grid',
|
||||
},
|
||||
actions: {
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flex: '0 1 auto',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
},
|
||||
}),
|
||||
)
|
||||
const useStyles = makeStyles()(() => ({
|
||||
root: {
|
||||
width: '100%',
|
||||
display: 'grid',
|
||||
},
|
||||
actions: {
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flex: '0 1 auto',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
},
|
||||
}))
|
||||
|
||||
export function AccountStamps(): ReactElement {
|
||||
const classes = useStyles()
|
||||
const { classes } = useStyles()
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import { Box, Grid, Typography } from '@material-ui/core'
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { Box, Grid, Typography } from '@mui/material'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import Download from 'remixicon-react/DownloadLineIcon'
|
||||
import Gift from 'remixicon-react/GiftLineIcon'
|
||||
import Link from 'remixicon-react/LinkIcon'
|
||||
|
||||
import ExpandableListItem from '../../../components/ExpandableListItem'
|
||||
import ExpandableListItemActions from '../../../components/ExpandableListItemActions'
|
||||
import ExpandableListItemKey from '../../../components/ExpandableListItemKey'
|
||||
import { SwarmButton } from '../../../components/SwarmButton'
|
||||
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext, CheckState } from '../../../providers/Bee'
|
||||
import { WalletInfoCard } from '../../../pages/info/WalletInfoCard'
|
||||
import { CheckState, Context as BeeContext } from '../../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../../providers/Settings'
|
||||
import { ROUTES } from '../../../routes'
|
||||
import { AccountNavigation } from '../AccountNavigation'
|
||||
import { Header } from '../Header'
|
||||
import { WalletInfoCard } from '../../../pages/info/WalletInfoCard'
|
||||
|
||||
export function AccountWallet(): ReactElement {
|
||||
const { nodeAddresses, nodeInfo, status, walletBalance } = useContext(BeeContext)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BZZ } from '@ethersphere/bee-js'
|
||||
import type { ReactElement } from 'react'
|
||||
|
||||
import CashoutModal from '../../components/CashoutModal'
|
||||
import ExpandableList from '../../components/ExpandableList'
|
||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
import { FdpStorage } from '@fairdatasociety/fdp-storage'
|
||||
import { Checkbox, InputBase, Typography } from '@material-ui/core'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { useEffect, useState } from 'react'
|
||||
import RegisterIcon from 'remixicon-react/AddBoxLineIcon'
|
||||
import LoginIcon from 'remixicon-react/LoginBoxLineIcon'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { Horizontal } from './Horizontal'
|
||||
import { Vertical } from './Vertical'
|
||||
|
||||
interface Props {
|
||||
fdp: FdpStorage
|
||||
onSuccessfulLogin: () => void
|
||||
}
|
||||
|
||||
export function FdpLogin({ fdp, onSuccessfulLogin }: Props) {
|
||||
const [username, setUsername] = useState<string>('')
|
||||
const [password, setPassword] = useState<string>('')
|
||||
const [remember, setRemember] = useState<boolean>(false)
|
||||
const [sepolia, setSepolia] = useState<string>('https://sepolia.drpc.org')
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
const inputStyle = { background: 'white', padding: '2px 8px', width: '100%' }
|
||||
|
||||
useEffect(() => {
|
||||
const storedSepolia = localStorage.getItem('sepolia')
|
||||
|
||||
if (storedSepolia) {
|
||||
setSepolia(storedSepolia)
|
||||
}
|
||||
const fdpCredentials = localStorage.getItem('fdpCredentials')
|
||||
|
||||
if (fdpCredentials) {
|
||||
const { username, password } = JSON.parse(fdpCredentials)
|
||||
setUsername(username)
|
||||
setPassword(password)
|
||||
setRemember(true)
|
||||
}
|
||||
}, [])
|
||||
|
||||
async function onLogin() {
|
||||
localStorage.setItem('sepolia', sepolia)
|
||||
|
||||
if (remember) {
|
||||
localStorage.setItem('fdpCredentials', JSON.stringify({ username, password }))
|
||||
} else {
|
||||
localStorage.removeItem('fdpCredentials')
|
||||
}
|
||||
enqueueSnackbar('Logging in...', { variant: 'info' })
|
||||
try {
|
||||
await fdp.account.login(username, password)
|
||||
enqueueSnackbar('Logged in successfully', { variant: 'success' })
|
||||
onSuccessfulLogin()
|
||||
} catch {
|
||||
enqueueSnackbar('Login failed', { variant: 'error' })
|
||||
} finally {
|
||||
setUsername('')
|
||||
setPassword('')
|
||||
setRemember(false)
|
||||
}
|
||||
}
|
||||
|
||||
function onRegister() {
|
||||
window.open('https://create.fairdatasociety.org/', '_blank')
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
maxWidth: '500px',
|
||||
margin: 'auto',
|
||||
}}
|
||||
>
|
||||
<Vertical gap={16} full>
|
||||
<Vertical gap={8} left full>
|
||||
<Typography variant="body2">Sepolia JSON RPC</Typography>
|
||||
<InputBase value={sepolia} onChange={e => setSepolia(e.target.value)} style={inputStyle} />
|
||||
</Vertical>
|
||||
<Vertical gap={8} left full>
|
||||
<Typography variant="body2">Username</Typography>
|
||||
<InputBase value={username} onChange={e => setUsername(e.target.value)} style={inputStyle} />
|
||||
</Vertical>
|
||||
<Vertical gap={8} left full>
|
||||
<Typography variant="body2">Password</Typography>
|
||||
<InputBase value={password} onChange={e => setPassword(e.target.value)} style={inputStyle} type="password" />
|
||||
</Vertical>
|
||||
<Vertical gap={8} left full>
|
||||
<Horizontal>
|
||||
<Checkbox checked={remember} onChange={e => setRemember(e.target.checked)} />
|
||||
<Typography variant="body2">Remember me</Typography>
|
||||
</Horizontal>
|
||||
</Vertical>
|
||||
<Vertical left full>
|
||||
<Horizontal gap={4}>
|
||||
<SwarmButton iconType={LoginIcon} onClick={onLogin}>
|
||||
Login
|
||||
</SwarmButton>
|
||||
<SwarmButton iconType={RegisterIcon} onClick={onRegister}>
|
||||
Registration
|
||||
</SwarmButton>
|
||||
</Horizontal>
|
||||
</Vertical>
|
||||
</Vertical>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import { FdpStorage } from '@fairdatasociety/fdp-storage'
|
||||
import { useState } from 'react'
|
||||
import { CafeReactFs } from '../../react-fs/CafeReactFs'
|
||||
import { FsItem, FsItemType } from '../../react-fs/CafeReactType'
|
||||
import { joinUrl } from '../../react-fs/Utility'
|
||||
|
||||
interface Props {
|
||||
fdp: FdpStorage
|
||||
name: string
|
||||
}
|
||||
|
||||
export function FdpPod({ fdp, name }: Props) {
|
||||
const [reloader, setReloader] = useState(0)
|
||||
|
||||
function reload() {
|
||||
setReloader(reloader + 1)
|
||||
}
|
||||
|
||||
return (
|
||||
<CafeReactFs
|
||||
rootAlias={`/${name}`}
|
||||
backgroundColor="#ffffff"
|
||||
reloader={reloader}
|
||||
onDeleteFile={async (path: string) => {
|
||||
await fdp.file.delete(name, path)
|
||||
reload()
|
||||
}}
|
||||
onDeleteDirectory={async (path: string) => {
|
||||
await fdp.directory.delete(name, path)
|
||||
reload()
|
||||
}}
|
||||
onUpload={(path: string) => {
|
||||
const input = document.createElement('input')
|
||||
input.type = 'file'
|
||||
input.multiple = true
|
||||
input.click()
|
||||
|
||||
return new Promise<void>(resolve => {
|
||||
input.onchange = async () => {
|
||||
if (!input.files || !input.files.length) {
|
||||
resolve()
|
||||
|
||||
return
|
||||
}
|
||||
for (const file of Array.from(input.files)) {
|
||||
const data = await file.arrayBuffer()
|
||||
await fdp.file.uploadData(name, joinUrl(path, file.name), new Uint8Array(data))
|
||||
}
|
||||
reload()
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}}
|
||||
onCreateDirectory={async (path: string) => {
|
||||
// eslint-disable-next-line no-alert
|
||||
const newDirectoryName = prompt('Directory name')
|
||||
|
||||
if (!newDirectoryName) {
|
||||
return
|
||||
}
|
||||
await fdp.directory.create(name, joinUrl(path, newDirectoryName))
|
||||
reload()
|
||||
}}
|
||||
// eslint-disable-next-line require-await
|
||||
onSync={async () => {
|
||||
setReloader(reloader + 1)
|
||||
}}
|
||||
download={async (path: string) => {
|
||||
const data = await fdp.file.downloadData(name, path)
|
||||
const url = URL.createObjectURL(new Blob([data]))
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = path.split('/').pop() || 'Untitled'
|
||||
a.click()
|
||||
}}
|
||||
list={async (path: string) => {
|
||||
const fdpResponse = await fdp.directory.read(name, path)
|
||||
const items: FsItem[] = []
|
||||
for (const directory of fdpResponse.directories) {
|
||||
items.push({
|
||||
name: directory.name,
|
||||
$type: FsItemType.DIRECTORY,
|
||||
id: directory.name,
|
||||
})
|
||||
}
|
||||
for (const file of fdpResponse.files) {
|
||||
items.push({
|
||||
name: file.name,
|
||||
$type: FsItemType.FILE,
|
||||
id: file.name,
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { FdpStorage } from '@fairdatasociety/fdp-storage'
|
||||
import { Pod } from '@fairdatasociety/fdp-storage/dist/pod/types'
|
||||
import { CircularProgress, Typography } from '@material-ui/core'
|
||||
import { FdpPod } from './FdpPod'
|
||||
import { Vertical } from './Vertical'
|
||||
|
||||
interface Props {
|
||||
fdp: FdpStorage
|
||||
pods: Pod[]
|
||||
loadingPods: boolean
|
||||
}
|
||||
|
||||
export function FdpPods({ fdp, pods, loadingPods }: Props) {
|
||||
if (loadingPods) {
|
||||
return (
|
||||
<Vertical gap={32} full>
|
||||
<CircularProgress />
|
||||
<Typography>Loading your pods...</Typography>
|
||||
</Vertical>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Vertical gap={16} full left>
|
||||
{pods.map(pod => (
|
||||
<FdpPod key={pod.index} fdp={fdp} name={pod.name} />
|
||||
))}
|
||||
</Vertical>
|
||||
)
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
interface Props {
|
||||
children: React.ReactNode
|
||||
p?: string
|
||||
gap?: number
|
||||
between?: boolean
|
||||
background?: string
|
||||
}
|
||||
|
||||
export function Horizontal({ children, p = '0', gap = 8, between, background }: Props) {
|
||||
const style = {
|
||||
display: 'flex',
|
||||
flexDirection: 'row' as 'row', //eslint-disable-line
|
||||
alignItems: 'center',
|
||||
justifyContent: between ? 'space-between' : 'flex-start',
|
||||
gap: `${gap}px`,
|
||||
padding: p,
|
||||
background,
|
||||
width: between ? '100%' : 'auto',
|
||||
}
|
||||
|
||||
return <div style={style}>{children}</div>
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
interface Props {
|
||||
children: React.ReactNode
|
||||
p?: number
|
||||
gap?: number
|
||||
left?: boolean
|
||||
full?: boolean
|
||||
}
|
||||
|
||||
export function Vertical({ children, p = 0, gap = 0, left = false, full = false }: Props) {
|
||||
const style = {
|
||||
display: 'flex',
|
||||
flexDirection: 'column' as 'column', //eslint-disable-line
|
||||
alignItems: left ? 'flex-start' : 'center',
|
||||
gap: `${gap}px`,
|
||||
width: full ? '100%' : 'auto',
|
||||
padding: `${p}px`,
|
||||
}
|
||||
|
||||
return <div style={style}>{children}</div>
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
import { FdpStorage } from '@fairdatasociety/fdp-storage'
|
||||
import { Pod } from '@fairdatasociety/fdp-storage/dist/pod/types'
|
||||
import { CircularProgress, Typography } from '@material-ui/core'
|
||||
import { Bee, MantarayNode } from '@ethersphere/bee-js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useEffect, useState } from 'react'
|
||||
import ImportIcon from 'remixicon-react/AddBoxLineIcon'
|
||||
import PlusCircle from 'remixicon-react/AddCircleLineIcon'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { joinUrl } from '../../react-fs/Utility'
|
||||
import { FdpLogin } from './FdpLogin'
|
||||
import { FdpPods } from './FdpPods'
|
||||
import { Horizontal } from './Horizontal'
|
||||
import { Vertical } from './Vertical'
|
||||
|
||||
async function makeFdp(): Promise<FdpStorage | null> {
|
||||
const bee = new Bee('http://localhost:1633')
|
||||
const sepolia = localStorage.getItem('sepolia') ?? 'https://sepolia.drpc.org'
|
||||
const postageBatches = await bee.getAllPostageBatch()
|
||||
const usableBatches = postageBatches.filter(batch => batch.usable)
|
||||
const highestCapacityBatch = usableBatches.length ? usableBatches.reduce((a, b) => (a.depth > b.depth ? a : b)) : null
|
||||
|
||||
if (!highestCapacityBatch) {
|
||||
return null
|
||||
}
|
||||
|
||||
// TODO: FDS has bad types
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return new FdpStorage('http://localhost:1633', highestCapacityBatch.batchID.toHex() as any, {
|
||||
ensOptions: {
|
||||
rpcUrl: sepolia,
|
||||
contractAddresses: {
|
||||
ensRegistry: '0x42a96D45d787685ac4b36292d218B106Fb39be7F',
|
||||
fdsRegistrar: '0xFBF00389140C00384d88d458239833E3231a7414',
|
||||
nameResolver: '0xE20ECe6Ea93c4edE41e4d3B973f6679F1E89986A',
|
||||
publicResolver: '0xC904989B579c2B216A75723688C784038AA99B56',
|
||||
reverseResolver: '0xbDC8D98d3cbFd68EA9c165E1f15Df6e77A2ae0C5',
|
||||
},
|
||||
gasEstimation: 1,
|
||||
performChecks: true,
|
||||
},
|
||||
providerOptions: {
|
||||
url: sepolia,
|
||||
},
|
||||
ensDomain: 'fds',
|
||||
})
|
||||
}
|
||||
|
||||
export default function FDP(): ReactElement {
|
||||
const [fdp, setFdp] = useState<FdpStorage | null>(null)
|
||||
const [pods, setPods] = useState<Pod[]>([])
|
||||
const [loggedIn, setLoggedIn] = useState<boolean>(false)
|
||||
const [loadingPods, setLoadingPods] = useState<boolean>(false)
|
||||
const [creatingPod, setCreatingPod] = useState<boolean>(false)
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
useEffect(() => {
|
||||
makeFdp().then(fdp => {
|
||||
if (!fdp) {
|
||||
enqueueSnackbar('FDP could not be initialized. Do you have a postage batch?', { variant: 'error' })
|
||||
}
|
||||
setFdp(fdp)
|
||||
})
|
||||
}, [enqueueSnackbar])
|
||||
|
||||
useEffect(() => {
|
||||
if (fdp && loggedIn) {
|
||||
setLoadingPods(true)
|
||||
fdp.personalStorage.list().then(pods => {
|
||||
setPods(pods.pods)
|
||||
setLoadingPods(false)
|
||||
})
|
||||
}
|
||||
}, [fdp, loggedIn])
|
||||
|
||||
function onSuccessfulLogin() {
|
||||
setLoggedIn(true)
|
||||
}
|
||||
|
||||
function onCreatePod() {
|
||||
if (!fdp) {
|
||||
return
|
||||
}
|
||||
|
||||
if (loadingPods || creatingPod) {
|
||||
enqueueSnackbar('Please wait until the pods are loaded', { variant: 'info' })
|
||||
|
||||
return
|
||||
}
|
||||
// eslint-disable-next-line no-alert
|
||||
const name = prompt('Enter a name for the new pod')
|
||||
|
||||
if (name) {
|
||||
setCreatingPod(true)
|
||||
fdp.personalStorage.create(name).then(() => {
|
||||
fdp.personalStorage.list().then(pods => {
|
||||
setPods(pods.pods)
|
||||
setCreatingPod(false)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function onImportPod() {
|
||||
if (!fdp) {
|
||||
return
|
||||
}
|
||||
|
||||
if (loadingPods || creatingPod) {
|
||||
enqueueSnackbar('Please wait until the pods are loaded', { variant: 'info' })
|
||||
|
||||
return
|
||||
}
|
||||
// eslint-disable-next-line no-alert
|
||||
const name = prompt('Enter a name for the new pod')
|
||||
// eslint-disable-next-line no-alert
|
||||
const importHash = prompt('Enter the Swarm reference')
|
||||
|
||||
if (!name || !importHash) {
|
||||
return
|
||||
}
|
||||
setCreatingPod(true)
|
||||
const bee = new Bee('http://localhost:1633')
|
||||
const manifest = await MantarayNode.unmarshal(bee, importHash)
|
||||
await manifest.loadRecursively(bee)
|
||||
const nodes = manifest.collect()
|
||||
await fdp.personalStorage.create(name)
|
||||
for (const node of nodes) {
|
||||
await fdp.file.uploadData(
|
||||
name,
|
||||
joinUrl('/', node.fullPathString),
|
||||
(await bee.downloadData(node.targetAddress)).toUint8Array(),
|
||||
)
|
||||
}
|
||||
const pods = await fdp.personalStorage.list()
|
||||
setPods(pods.pods)
|
||||
setCreatingPod(false)
|
||||
}
|
||||
|
||||
if (!fdp) {
|
||||
return <CircularProgress />
|
||||
}
|
||||
|
||||
return (
|
||||
<Vertical gap={32} full left>
|
||||
<Horizontal between>
|
||||
<Typography variant="h1">Files</Typography>
|
||||
{loggedIn && (
|
||||
<Horizontal gap={4}>
|
||||
<SwarmButton onClick={onCreatePod} iconType={PlusCircle}>
|
||||
Create
|
||||
</SwarmButton>
|
||||
<SwarmButton onClick={onImportPod} iconType={ImportIcon}>
|
||||
Import
|
||||
</SwarmButton>
|
||||
</Horizontal>
|
||||
)}
|
||||
</Horizontal>
|
||||
{!loggedIn && <FdpLogin fdp={fdp} onSuccessfulLogin={onSuccessfulLogin} />}
|
||||
{loggedIn && <FdpPods fdp={fdp} pods={pods} loadingPods={loadingPods || creatingPod} />}
|
||||
{loggedIn && !loadingPods && !creatingPod && pods.length === 0 && (
|
||||
<Typography>
|
||||
<strong>You do not have any pods yet.</strong> Get started by clicking the Create or Import button on the top
|
||||
right.
|
||||
</Typography>
|
||||
)}
|
||||
</Vertical>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Box, Grid, Typography } from '@material-ui/core'
|
||||
import { NULL_TOPIC } from '@ethersphere/bee-js'
|
||||
import { Box, Grid, Typography } from '@mui/material'
|
||||
import { Form, Formik } from 'formik'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
import X from 'remixicon-react/CloseLineIcon'
|
||||
|
||||
import { DocumentationText } from '../../components/DocumentationText'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
@@ -26,7 +27,7 @@ interface FormValues {
|
||||
|
||||
const initialValues: FormValues = {
|
||||
identityName: '',
|
||||
type: 'PRIVATE_KEY',
|
||||
type: IdentityType.PrivateKey,
|
||||
password: '',
|
||||
}
|
||||
|
||||
@@ -102,12 +103,12 @@ export default function CreateNewFeed(): ReactElement {
|
||||
formik
|
||||
name="type"
|
||||
options={[
|
||||
{ label: 'Keypair Only', value: 'PRIVATE_KEY' },
|
||||
{ label: 'Password Protected', value: 'V3' },
|
||||
{ label: 'Keypair Only', value: IdentityType.PrivateKey },
|
||||
{ label: 'Password Protected', value: IdentityType.V3 },
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
{values.type === 'V3' && <SwarmTextInput name="password" label="Password" password formik />}
|
||||
{values.type === IdentityType.V3 && <SwarmTextInput name="password" label="Password" password formik />}
|
||||
<Box mt={2}>
|
||||
<ExpandableListItemKey label="Topic" value={NULL_TOPIC.toHex()} />
|
||||
</Box>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { ReactElement } from 'react'
|
||||
import X from 'remixicon-react/CloseLineIcon'
|
||||
import Trash from 'remixicon-react/DeleteBin7LineIcon'
|
||||
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { SwarmDialog } from '../../components/SwarmDialog'
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import { Box, createStyles, makeStyles, Typography } from '@material-ui/core'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { saveAs } from 'file-saver'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement } from 'react'
|
||||
import Download from 'remixicon-react/DownloadLineIcon'
|
||||
import Clipboard from 'remixicon-react/ClipboardLineIcon'
|
||||
import Download from 'remixicon-react/DownloadLineIcon'
|
||||
import { makeStyles } from 'tss-react/mui'
|
||||
|
||||
import { Code } from '../../components/Code'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { SwarmDialog } from '../../components/SwarmDialog'
|
||||
import { TitleWithClose } from '../../components/TitleWithClose'
|
||||
import { Identity } from '../../providers/Feeds'
|
||||
import { Identity, IdentityType } from '../../providers/Feeds'
|
||||
|
||||
interface Props {
|
||||
identity: Identity
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
wrapper: {
|
||||
maxWidth: '100%',
|
||||
},
|
||||
}),
|
||||
)
|
||||
const useStyles = makeStyles()(() => ({
|
||||
wrapper: {
|
||||
maxWidth: '100%',
|
||||
},
|
||||
}))
|
||||
|
||||
export function ExportFeedDialog({ identity, onClose }: Props): ReactElement {
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
const classes = useStyles()
|
||||
const { classes } = useStyles()
|
||||
|
||||
function onDownload() {
|
||||
saveAs(
|
||||
@@ -39,7 +39,7 @@ export function ExportFeedDialog({ identity, onClose }: Props): ReactElement {
|
||||
}
|
||||
|
||||
function getExportText() {
|
||||
return identity.type === 'V3' ? 'JSON file' : 'the private key string'
|
||||
return identity.type === IdentityType.V3 ? 'JSON file' : 'the private key string'
|
||||
}
|
||||
|
||||
function onCopy() {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { ReactElement, useState } from 'react'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
import X from 'remixicon-react/CloseLineIcon'
|
||||
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { SwarmDialog } from '../../components/SwarmDialog'
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Box } from '@material-ui/core'
|
||||
import { Box } from '@mui/material'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import X from 'remixicon-react/CloseLineIcon'
|
||||
|
||||
import { DocumentationText } from '../../components/DocumentationText'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
@@ -11,6 +12,7 @@ import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as IdentityContext } from '../../providers/Feeds'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { FileOrigin } from '../files/FileNavigation'
|
||||
import { UploadArea } from '../files/UploadArea'
|
||||
|
||||
export function FeedSubpage(): ReactElement {
|
||||
@@ -32,11 +34,10 @@ export function FeedSubpage(): ReactElement {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
beeApi?.downloadData(identity.feedHash).then(() => setAvailable(true))
|
||||
} catch {
|
||||
setAvailable(false)
|
||||
}
|
||||
beeApi
|
||||
?.downloadData(identity.feedHash)
|
||||
.then(() => setAvailable(true))
|
||||
.catch(() => setAvailable(false))
|
||||
}, [beeApi, uuid, identity, navigate])
|
||||
|
||||
if (!identity || !status.all) {
|
||||
@@ -50,7 +51,7 @@ export function FeedSubpage(): ReactElement {
|
||||
return (
|
||||
<div>
|
||||
<HistoryHeader>{`${identity.name} Website`}</HistoryHeader>
|
||||
<UploadArea showHelp={false} uploadOrigin={{ origin: 'FEED', uuid }} />
|
||||
<UploadArea showHelp={false} uploadOrigin={{ origin: FileOrigin.Feed, uuid }} />
|
||||
{available && identity.feedHash ? (
|
||||
<>
|
||||
<Box mb={4}>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Box, createStyles, makeStyles, TextareaAutosize, Theme } from '@material-ui/core'
|
||||
import { Box, TextareaAutosize } from '@mui/material'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import React, { ReactElement, useContext, useRef, useState } from 'react'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
import Upload from 'remixicon-react/UploadLineIcon'
|
||||
import { makeStyles } from 'tss-react/mui'
|
||||
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { SwarmDialog } from '../../components/SwarmDialog'
|
||||
@@ -15,18 +17,16 @@ interface Props {
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
textarea: {
|
||||
width: '100%',
|
||||
border: 0,
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
displayNone: {
|
||||
display: 'none',
|
||||
},
|
||||
}),
|
||||
)
|
||||
const useStyles = makeStyles()(theme => ({
|
||||
textarea: {
|
||||
width: '100%',
|
||||
border: 0,
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
displayNone: {
|
||||
display: 'none',
|
||||
},
|
||||
}))
|
||||
|
||||
export function ImportFeedDialog({ onClose }: Props): ReactElement {
|
||||
const [textareaValue, setTextareaValue] = useState('')
|
||||
@@ -37,7 +37,7 @@ export function ImportFeedDialog({ onClose }: Props): ReactElement {
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
const classes = useStyles()
|
||||
const { classes } = useStyles()
|
||||
|
||||
async function onImport() {
|
||||
const feed = await importIdentity(name, textareaValue)
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import { Box, Grid, Typography } from '@material-ui/core'
|
||||
import { Box, Grid, Typography } from '@mui/material'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import { useNavigate, useParams } from 'react-router'
|
||||
import Bookmark from 'remixicon-react/BookmarkLineIcon'
|
||||
import X from 'remixicon-react/CloseLineIcon'
|
||||
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { SelectEvent, SwarmSelect } from '../../components/SwarmSelect'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Identity, Context as IdentityContext } from '../../providers/Feeds'
|
||||
import { Context as IdentityContext, Identity, IdentityType } from '../../providers/Feeds'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context as StampContext } from '../../providers/Stamps'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { persistIdentity, updateFeed } from '../../utils/identity'
|
||||
|
||||
import { FeedPasswordDialog } from './FeedPasswordDialog'
|
||||
|
||||
export default function UpdateFeed(): ReactElement {
|
||||
@@ -56,7 +58,7 @@ export default function UpdateFeed(): ReactElement {
|
||||
return
|
||||
}
|
||||
|
||||
if (selectedIdentity.type === 'V3') {
|
||||
if (selectedIdentity.type === IdentityType.V3) {
|
||||
setShowPasswordPrompt(true)
|
||||
} else {
|
||||
onFeedUpdate(selectedIdentity)
|
||||
@@ -73,8 +75,15 @@ export default function UpdateFeed(): ReactElement {
|
||||
return
|
||||
}
|
||||
|
||||
if (!hash) {
|
||||
enqueueSnackbar(<span>Hash is invalid</span>, { variant: 'error' })
|
||||
setLoading(false)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await updateFeed(beeApi, identity, hash!, selectedStamp, password as string) // eslint-disable-line
|
||||
await updateFeed(beeApi, identity, hash, selectedStamp, password as string)
|
||||
persistIdentity(identities, identity)
|
||||
setIdentities([...identities])
|
||||
navigate(ROUTES.ACCOUNT_FEEDS_VIEW.replace(':uuid', identity.uuid))
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { NULL_TOPIC } from '@ethersphere/bee-js'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
|
||||
import Trash from 'remixicon-react/DeleteBin7LineIcon'
|
||||
import Download from 'remixicon-react/DownloadLineIcon'
|
||||
import Info from 'remixicon-react/InformationLineIcon'
|
||||
|
||||
import ExpandableList from '../../components/ExpandableList'
|
||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext, CheckState } from '../../providers/Bee'
|
||||
import { Identity, Context as IdentityContext } from '../../providers/Feeds'
|
||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as IdentityContext, Identity } from '../../providers/Feeds'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { formatEnum } from '../../utils'
|
||||
import { persistIdentitiesWithoutUpdate } from '../../utils/identity'
|
||||
|
||||
import { DeleteFeedDialog } from './DeleteFeedDialog'
|
||||
import { ExportFeedDialog } from './ExportFeedDialog'
|
||||
import { ImportFeedDialog } from './ImportFeedDialog'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createContext, useContext, useMemo, useRef, useState, ReactNode, useCallback } from 'react'
|
||||
import { createContext, ReactNode, useCallback, useContext, useMemo, useRef, useState } from 'react'
|
||||
|
||||
type Scope = 'selected' | 'all'
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createContext, useContext, useState, ReactNode } from 'react'
|
||||
import { createContext, ReactNode, useContext, useState } from 'react'
|
||||
|
||||
import { ViewType } from '../../modules/filemanager/constants/transfers'
|
||||
|
||||
interface ViewContextProps {
|
||||
|
||||
+182
-114
@@ -1,22 +1,157 @@
|
||||
import { ReactElement, useContext, useEffect, useState, useRef, useCallback, useMemo } from 'react'
|
||||
import './FileManager.scss'
|
||||
import { DriveInfo, FileManagerBase } from '@solarpunkltd/file-manager-lib'
|
||||
import { ReactElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import { SearchProvider } from './SearchContext'
|
||||
import { ViewProvider } from './ViewContext'
|
||||
import { Header } from '../../modules/filemanager/components/Header/Header'
|
||||
import { Sidebar } from '../../modules/filemanager/components/Sidebar/Sidebar'
|
||||
import { AdminStatusBar } from '../../modules/filemanager/components/AdminStatusBar/AdminStatusBar'
|
||||
import { FileBrowser } from '../../modules/filemanager/components/FileBrowser/FileBrowser'
|
||||
import { InitialModal } from '../../modules/filemanager/components/InitialModal/InitialModal'
|
||||
import { Context as FMContext } from '../../providers/FileManager'
|
||||
import { BrowserPlatform, cacheClearUrls, detectBrowser } from '../../providers/Platform'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context as BeeContext, CheckState } from '../../providers/Bee'
|
||||
import { PrivateKeyModal } from '../../modules/filemanager/components/PrivateKeyModal/PrivateKeyModal'
|
||||
import { getSignerPk, removeSignerPk } from '../../../src/modules/filemanager/utils/common'
|
||||
import { ErrorModal } from '../../../src/modules/filemanager/components/ErrorModal/ErrorModal'
|
||||
import { ConfirmModal } from '../../modules/filemanager/components/ConfirmModal/ConfirmModal'
|
||||
import { Button } from '../../modules/filemanager/components/Button/Button'
|
||||
import { FormbricksIntegration } from '../../modules/filemanager/components/FormbricksIntegration/FormbricksIntegration'
|
||||
|
||||
import './FileManager.scss'
|
||||
|
||||
import { AdminStatusBar } from '@/modules/filemanager/components/AdminStatusBar/AdminStatusBar'
|
||||
import { Button } from '@/modules/filemanager/components/Button/Button'
|
||||
import { ConfirmModal } from '@/modules/filemanager/components/ConfirmModal/ConfirmModal'
|
||||
import { ErrorModal } from '@/modules/filemanager/components/ErrorModal/ErrorModal'
|
||||
import { FileBrowser } from '@/modules/filemanager/components/FileBrowser/FileBrowser'
|
||||
import { FormbricksIntegration } from '@/modules/filemanager/components/FormbricksIntegration/FormbricksIntegration'
|
||||
import { Header } from '@/modules/filemanager/components/Header/Header'
|
||||
import { InitialModal } from '@/modules/filemanager/components/InitialModal/InitialModal'
|
||||
import { PrivateKeyModal } from '@/modules/filemanager/components/PrivateKeyModal/PrivateKeyModal'
|
||||
import { Sidebar } from '@/modules/filemanager/components/Sidebar/Sidebar'
|
||||
import { getSignerPk, removeSignerPk } from '@/modules/filemanager/utils/common'
|
||||
import { CheckState, Context as BeeContext } from '@/providers/Bee'
|
||||
import { Context as FMContext } from '@/providers/FileManager'
|
||||
import { BrowserPlatform, cacheClearUrls, detectBrowser } from '@/providers/Platform'
|
||||
import { Context as SettingsContext } from '@/providers/Settings'
|
||||
|
||||
function PrivateKeyModalBlock({ onSaved }: { onSaved: () => void }) {
|
||||
return (
|
||||
<div className="fm-main">
|
||||
<PrivateKeyModal onSaved={onSaved} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function InitializationErrorBlock({ onOk }: { onOk: () => void }) {
|
||||
return (
|
||||
<div className="fm-main">
|
||||
<div className="fm-loading">
|
||||
<div className="fm-loading-title">Failed to initialize File Manager, reload and try again </div>
|
||||
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '16px' }}>
|
||||
<div style={{ minWidth: '120px' }}>
|
||||
<Button label={'OK'} variant="primary" disabled={false} onClick={onOk} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ResetModalBlock({ cacheHelpUrl, onConfirm }: { cacheHelpUrl: string; onConfirm: () => void }) {
|
||||
return (
|
||||
<div className="fm-main">
|
||||
<ConfirmModal
|
||||
title="Reset File Manager State"
|
||||
message={
|
||||
<span>
|
||||
Your File Manager state appears invalid. Please{' '}
|
||||
<a
|
||||
href={cacheHelpUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ display: 'inline', textDecoration: 'underline' }}
|
||||
>
|
||||
clear the browser cache
|
||||
</a>{' '}
|
||||
and reload the page. Then you can reset the File Manager to continue.
|
||||
</span>
|
||||
}
|
||||
confirmLabel="Continue"
|
||||
onConfirm={onConfirm}
|
||||
background={false}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function InitialModalBlock(props: {
|
||||
resetState: boolean
|
||||
handleVisibility: (isVisible: boolean) => void
|
||||
handleShowError: (flag: boolean, error?: string) => void
|
||||
setIsCreationInProgress: (isCreating: boolean) => void
|
||||
}) {
|
||||
return (
|
||||
<div className="fm-main">
|
||||
<InitialModal {...props} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function LoadingBlock() {
|
||||
return (
|
||||
<div className="fm-main">
|
||||
<div className="fm-loading" aria-live="polite">
|
||||
<div className="fm-spinner" aria-hidden="true" />
|
||||
<div className="fm-loading-title">File manager loading…</div>
|
||||
<div className="fm-loading-subtitle">Please wait a few seconds</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ErrorModalBlock({ onClick, label }: { onClick: () => void; label: string }) {
|
||||
return <ErrorModal label={label} onClick={onClick} />
|
||||
}
|
||||
|
||||
function FileManagerMainContent(props: {
|
||||
fm: FileManagerBase | null
|
||||
showConnectionError: boolean
|
||||
setShowConnectionError: (v: boolean) => void
|
||||
isFormbricksActive: boolean
|
||||
errorMessage: string
|
||||
setErrorMessage: (msg: string) => void
|
||||
loading: boolean
|
||||
adminDrive: DriveInfo | null
|
||||
isCreationInProgress: boolean
|
||||
}) {
|
||||
const {
|
||||
fm,
|
||||
showConnectionError,
|
||||
setShowConnectionError,
|
||||
isFormbricksActive,
|
||||
errorMessage,
|
||||
setErrorMessage,
|
||||
loading,
|
||||
adminDrive,
|
||||
isCreationInProgress,
|
||||
} = props
|
||||
|
||||
return (
|
||||
<SearchProvider>
|
||||
<ViewProvider>
|
||||
<div className="fm-main">
|
||||
{showConnectionError && fm && (
|
||||
<ErrorModal
|
||||
label="Bee node connection error. Please check your node status. File Manager will continue when connection is restored."
|
||||
onClick={() => setShowConnectionError(false)}
|
||||
/>
|
||||
)}
|
||||
<FormbricksIntegration isActive={isFormbricksActive} />
|
||||
<Header />
|
||||
<div className="fm-main-content">
|
||||
<Sidebar errorMessage={errorMessage} setErrorMessage={setErrorMessage} loading={loading} />
|
||||
<FileBrowser errorMessage={errorMessage} setErrorMessage={setErrorMessage} />
|
||||
</div>
|
||||
<AdminStatusBar
|
||||
adminStamp={fm?.adminStamp || null}
|
||||
adminDrive={adminDrive}
|
||||
loading={loading}
|
||||
isCreationInProgress={isCreationInProgress}
|
||||
setErrorMessage={setErrorMessage}
|
||||
/>
|
||||
</div>
|
||||
</ViewProvider>
|
||||
</SearchProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export function FileManagerPage(): ReactElement {
|
||||
const isMountedRef = useRef(true)
|
||||
@@ -136,99 +271,46 @@ export function FileManagerPage(): ReactElement {
|
||||
const isFormbricksActive = Boolean(fm && fm.adminStamp && adminDrive && !showInitialModal && !loading)
|
||||
|
||||
if (!hasPk) {
|
||||
return (
|
||||
<div className="fm-main">
|
||||
<PrivateKeyModal onSaved={handlePrivateKeySaved} />
|
||||
</div>
|
||||
)
|
||||
return <PrivateKeyModalBlock onSaved={handlePrivateKeySaved} />
|
||||
}
|
||||
|
||||
if (initializationError && !isLoading && !shallReset) {
|
||||
return (
|
||||
<div className="fm-main">
|
||||
<div className="fm-loading">
|
||||
<div className="fm-loading-title">Failed to initialize File Manager, reload and try again </div>
|
||||
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '16px' }}>
|
||||
<div style={{ minWidth: '120px' }}>
|
||||
<Button
|
||||
label={'OK'}
|
||||
variant="primary"
|
||||
disabled={false}
|
||||
onClick={() => {
|
||||
removeSignerPk()
|
||||
setHasPk(false)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<InitializationErrorBlock
|
||||
onOk={() => {
|
||||
removeSignerPk()
|
||||
setHasPk(false)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (showResetModal) {
|
||||
return (
|
||||
<div className="fm-main">
|
||||
<ConfirmModal
|
||||
title="Reset File Manager State"
|
||||
message={
|
||||
<span>
|
||||
Your File Manager state appears invalid. Please{' '}
|
||||
<a
|
||||
href={cacheHelpUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ display: 'inline', textDecoration: 'underline' }}
|
||||
>
|
||||
clear the browser cache
|
||||
</a>{' '}
|
||||
and reload the page. Then you can reset the File Manager to continue.
|
||||
</span>
|
||||
}
|
||||
confirmLabel="Continue"
|
||||
onConfirm={() => {
|
||||
setShowResetModal(false)
|
||||
}}
|
||||
background={false}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
return <ResetModalBlock cacheHelpUrl={cacheHelpUrl} onConfirm={() => setShowResetModal(false)} />
|
||||
}
|
||||
|
||||
if (!showErrorModal && (isEmptyState || isInvalidState)) {
|
||||
return (
|
||||
<div className="fm-main">
|
||||
<InitialModal
|
||||
resetState={shallReset}
|
||||
handleVisibility={(isVisible: boolean) => setShowInitialModal(isVisible)}
|
||||
handleShowError={(flag: boolean, error?: string) => {
|
||||
setShowErrorModal(flag)
|
||||
<InitialModalBlock
|
||||
resetState={shallReset}
|
||||
handleVisibility={(isVisible: boolean) => setShowInitialModal(isVisible)}
|
||||
handleShowError={(flag: boolean, error?: string) => {
|
||||
setShowErrorModal(flag)
|
||||
|
||||
if (error) {
|
||||
setErrorMessage(error)
|
||||
}
|
||||
}}
|
||||
setIsCreationInProgress={(isCreating: boolean) => setIsCreationInProgress(isCreating)}
|
||||
/>
|
||||
</div>
|
||||
if (error) setErrorMessage(error)
|
||||
}}
|
||||
setIsCreationInProgress={(isCreating: boolean) => setIsCreationInProgress(isCreating)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (!fm) {
|
||||
return (
|
||||
<div className="fm-main">
|
||||
<div className="fm-loading" aria-live="polite">
|
||||
<div className="fm-spinner" aria-hidden="true" />
|
||||
<div className="fm-loading-title">File manager loading…</div>
|
||||
<div className="fm-loading-subtitle">Please wait a few seconds</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
return <LoadingBlock />
|
||||
}
|
||||
|
||||
if (showErrorModal) {
|
||||
return (
|
||||
<ErrorModal
|
||||
<ErrorModalBlock
|
||||
label={
|
||||
'Error creating Admin Drive. Please try again. Possible causes include insufficient xDAI balance or a lost connection to the RPC.'
|
||||
}
|
||||
@@ -242,30 +324,16 @@ export function FileManagerPage(): ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<SearchProvider>
|
||||
<ViewProvider>
|
||||
<div className="fm-main">
|
||||
{showConnectionError && fm && (
|
||||
<ErrorModal
|
||||
label="Bee node connection error. Please check your node status. File Manager will continue when connection is restored."
|
||||
onClick={() => setShowConnectionError(false)}
|
||||
/>
|
||||
)}
|
||||
<FormbricksIntegration isActive={isFormbricksActive} />
|
||||
<Header />
|
||||
<div className="fm-main-content">
|
||||
<Sidebar errorMessage={errorMessage} setErrorMessage={setErrorMessage} loading={loading} />
|
||||
<FileBrowser errorMessage={errorMessage} setErrorMessage={setErrorMessage} />
|
||||
</div>
|
||||
<AdminStatusBar
|
||||
adminStamp={fm?.adminStamp || null}
|
||||
adminDrive={adminDrive}
|
||||
loading={loading}
|
||||
isCreationInProgress={isCreationInProgress}
|
||||
setErrorMessage={setErrorMessage}
|
||||
/>
|
||||
</div>
|
||||
</ViewProvider>
|
||||
</SearchProvider>
|
||||
<FileManagerMainContent
|
||||
fm={fm}
|
||||
showConnectionError={showConnectionError}
|
||||
setShowConnectionError={() => setShowConnectionError(false)}
|
||||
isFormbricksActive={isFormbricksActive}
|
||||
errorMessage={errorMessage}
|
||||
setErrorMessage={setErrorMessage}
|
||||
loading={loading}
|
||||
adminDrive={adminDrive}
|
||||
isCreationInProgress={isCreationInProgress}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
import { StripedWrapper } from '../../components/StripedWrapper'
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,44 +1,45 @@
|
||||
import { Box, Grid, Typography } from '@material-ui/core'
|
||||
import { Web } from '@material-ui/icons'
|
||||
import { Web } from '@mui/icons-material'
|
||||
import { Box, Grid, Typography } from '@mui/material'
|
||||
import { ReactElement, useMemo } from 'react'
|
||||
import File from 'remixicon-react/FileLineIcon'
|
||||
import Folder from 'remixicon-react/FolderLineIcon'
|
||||
|
||||
import { FitAudio } from '../../components/FitAudio'
|
||||
import { FitImage } from '../../components/FitImage'
|
||||
import { FitVideo } from '../../components/FitVideo'
|
||||
import { shortenText } from '../../utils'
|
||||
import { getHumanReadableFileSize } from '../../utils/file'
|
||||
import { shortenHash } from '../../utils/hash'
|
||||
|
||||
import { AssetIcon } from './AssetIcon'
|
||||
import { FitVideo } from '../../components/FitVideo'
|
||||
import { FitAudio } from '../../components/FitAudio'
|
||||
|
||||
interface Props {
|
||||
previewUri?: string
|
||||
metadata?: Metadata
|
||||
}
|
||||
|
||||
/* eslint-disable react/display-name */
|
||||
const getPreviewComponent = (previewUri?: string, metadata?: Metadata) => {
|
||||
const getPreviewElement = (previewUri?: string, metadata?: Metadata) => {
|
||||
if (metadata?.isVideo) {
|
||||
return () => <FitVideo src={previewUri} maxWidth="250px" maxHeight="175px" />
|
||||
return <FitVideo src={previewUri} maxWidth="250px" maxHeight="175px" />
|
||||
}
|
||||
|
||||
if (metadata?.isAudio) {
|
||||
return () => <FitAudio src={previewUri} maxWidth="250px" />
|
||||
return <FitAudio src={previewUri} maxWidth="250px" />
|
||||
}
|
||||
|
||||
if (metadata?.isImage) {
|
||||
return () => <FitImage maxWidth="250px" maxHeight="175px" alt="Upload Preview" src={previewUri} />
|
||||
return <FitImage maxWidth="250px" maxHeight="175px" alt="Upload Preview" src={previewUri} />
|
||||
}
|
||||
|
||||
if (metadata?.isWebsite) {
|
||||
return () => <AssetIcon icon={<Web />} />
|
||||
return <AssetIcon icon={<Web />} />
|
||||
}
|
||||
|
||||
if (metadata?.type === 'folder') {
|
||||
return () => <AssetIcon icon={<Folder />} />
|
||||
return <AssetIcon icon={<Folder />} />
|
||||
}
|
||||
|
||||
return () => <AssetIcon icon={<File />} />
|
||||
return <AssetIcon icon={<File />} />
|
||||
}
|
||||
|
||||
const getType = (metadata?: Metadata) => {
|
||||
@@ -51,14 +52,14 @@ const getType = (metadata?: Metadata) => {
|
||||
|
||||
// TODO: add optional prop for indexDocument when it is already known (e.g. downloading a manifest)
|
||||
export function AssetPreview({ metadata, previewUri }: Props): ReactElement | null {
|
||||
const PreviewAssetComponent = useMemo(() => getPreviewComponent(previewUri, metadata), [metadata, previewUri])
|
||||
const previewElement = useMemo(() => getPreviewElement(previewUri, metadata), [metadata, previewUri])
|
||||
const type = useMemo(() => getType(metadata), [metadata])
|
||||
|
||||
return (
|
||||
<Box mb={4}>
|
||||
<Box bgcolor="background.paper">
|
||||
<Grid container direction="row">
|
||||
<PreviewAssetComponent />
|
||||
{previewElement}
|
||||
<Box p={2}>
|
||||
{metadata?.hash && <Typography>Swarm Hash: {shortenHash(metadata.hash)}</Typography>}
|
||||
{metadata?.name && metadata?.name !== metadata?.hash && (
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
import { Box } from '@material-ui/core'
|
||||
import { Reference } from '@ethersphere/bee-js'
|
||||
import { Box } from '@mui/material'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
import { DocumentationText } from '../../components/DocumentationText'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
import ExpandableListItemLink from '../../components/ExpandableListItemLink'
|
||||
|
||||
interface Props {
|
||||
isWebsite?: boolean
|
||||
reference: string
|
||||
reference?: string
|
||||
}
|
||||
|
||||
export function AssetSummary({ isWebsite, reference }: Props): ReactElement {
|
||||
const isHash = Reference.isValid(reference)
|
||||
export function AssetSummary({ reference }: Props): ReactElement {
|
||||
const isHash = reference ? Reference.isValid(reference) : false
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box mb={4}>
|
||||
{isHash && <ExpandableListItemKey label="Swarm hash" value={reference} />}
|
||||
{!isHash && <ExpandableListItemLink label="ENS" value={reference} />}
|
||||
{isHash && <ExpandableListItemKey label="Swarm hash" value={reference || ''} />}
|
||||
{!isHash && <ExpandableListItemLink label="ENS" value={reference || ''} />}
|
||||
</Box>
|
||||
<DocumentationText>
|
||||
The Swarm Gateway is graciously provided by the Swarm Foundation. This service is under development and provided
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { Box } from '@material-ui/core'
|
||||
import { Tag } from '@ethersphere/bee-js'
|
||||
import { ReactElement, useContext, useEffect, useRef, useState } from 'react'
|
||||
import { Box } from '@mui/material'
|
||||
import { ReactElement, useCallback, useContext, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { DocumentationText } from '../../components/DocumentationText'
|
||||
import { LinearProgressWithLabel } from '../../components/ProgressBar'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
|
||||
interface Props {
|
||||
reference: string
|
||||
reference?: string
|
||||
}
|
||||
|
||||
const SYNC_CHECK_INTERVAL_MS = 2000
|
||||
|
||||
export function AssetSyncing({ reference }: Props): ReactElement {
|
||||
const { beeApi } = useContext(SettingsContext)
|
||||
|
||||
@@ -16,8 +19,8 @@ export function AssetSyncing({ reference }: Props): ReactElement {
|
||||
const [isRetrieveChecking, setIsRetrieveChecking] = useState<boolean>(false)
|
||||
const [syncProgress, setSyncProgress] = useState<number>(0)
|
||||
|
||||
const syncCheck = async () => {
|
||||
if (!beeApi) return
|
||||
const syncCheck = useCallback(async () => {
|
||||
if (!beeApi || !reference) return
|
||||
|
||||
let allTags: Tag[] = []
|
||||
let offset = 0
|
||||
@@ -36,10 +39,10 @@ export function AssetSyncing({ reference }: Props): ReactElement {
|
||||
const progress = ((tag.seen + tag.synced) / tag.split) * 100
|
||||
setSyncProgress(progress)
|
||||
}
|
||||
}
|
||||
}, [beeApi, reference])
|
||||
|
||||
useEffect(() => {
|
||||
syncTimer.current = setInterval(syncCheck, 2000)
|
||||
syncTimer.current = setInterval(syncCheck, SYNC_CHECK_INTERVAL_MS)
|
||||
|
||||
return () => {
|
||||
if (syncTimer.current) {
|
||||
@@ -47,8 +50,7 @@ export function AssetSyncing({ reference }: Props): ReactElement {
|
||||
syncTimer.current = null
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [reference])
|
||||
}, [reference, syncCheck])
|
||||
|
||||
useEffect(() => {
|
||||
if (syncProgress === 100 && syncTimer.current) {
|
||||
@@ -63,7 +65,7 @@ export function AssetSyncing({ reference }: Props): ReactElement {
|
||||
To ensure it's not due to invalid synchronization data,
|
||||
verify availability from at least 70% using one of the stewardship endpoints.
|
||||
*/
|
||||
if (beeApi && !isRetrieveChecking && syncProgress > 10 && syncProgress < 100) {
|
||||
if (beeApi && reference && !isRetrieveChecking && syncProgress > 10 && syncProgress < 100) {
|
||||
// It's a long running task make sure only one run occurs at a time.
|
||||
setIsRetrieveChecking(true)
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { BeeModes, MantarayNode, NULL_ADDRESS, Reference } from '@ethersphere/bee-js'
|
||||
import { BeeModes, Reference } from '@ethersphere/bee-js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import Search from 'remixicon-react/SearchLineIcon'
|
||||
|
||||
import ExpandableListItemInput from '../../components/ExpandableListItemInput'
|
||||
import { History } from '../../components/History'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
@@ -10,8 +11,10 @@ import { Context as FileContext, defaultUploadOrigin } from '../../providers/Fil
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { recognizeEnsOrSwarmHash, regexpEns } from '../../utils'
|
||||
import { HISTORY_KEYS, determineHistoryName, putHistory } from '../../utils/local-storage'
|
||||
import { FileNavigation } from './FileNavigation'
|
||||
import { determineHistoryName, LocalStorageKeys, putHistory } from '../../utils/localStorage'
|
||||
import { loadManifest } from '../../utils/manifest'
|
||||
|
||||
import { FileNavigation, FileOrigin } from './FileNavigation'
|
||||
|
||||
export function Download(): ReactElement {
|
||||
const [loading, setLoading] = useState(false)
|
||||
@@ -43,22 +46,11 @@ export function Download(): ReactElement {
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
let manifest = await MantarayNode.unmarshal(beeApi, identifier)
|
||||
await manifest.loadRecursively(beeApi)
|
||||
|
||||
// If the manifest is a feed, resolve it and overwrite the manifest
|
||||
await manifest.resolveFeed(beeApi).then(
|
||||
async feed =>
|
||||
await feed.ifPresentAsync(async feedUpdate => {
|
||||
manifest = MantarayNode.unmarshalFromData(feedUpdate.payload.toUint8Array(), NULL_ADDRESS)
|
||||
await manifest.loadRecursively(beeApi)
|
||||
}),
|
||||
)
|
||||
|
||||
const manifest = await loadManifest(beeApi, identifier)
|
||||
const rootMetadata = manifest.getDocsMetadata()
|
||||
|
||||
putHistory(
|
||||
HISTORY_KEYS.DOWNLOAD_HISTORY,
|
||||
LocalStorageKeys.downloadHistory,
|
||||
identifier,
|
||||
determineHistoryName(identifier, rootMetadata.indexDocument),
|
||||
)
|
||||
@@ -74,7 +66,8 @@ export function Download(): ReactElement {
|
||||
if (message.includes('Not Found: Not Found')) {
|
||||
message = 'The specified hash was not found.'
|
||||
}
|
||||
console.error(error) // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error)
|
||||
enqueueSnackbar(<span>Error: {message || 'Unknown'}</span>, { variant: 'error' })
|
||||
} finally {
|
||||
setLoading(false)
|
||||
@@ -83,7 +76,7 @@ export function Download(): ReactElement {
|
||||
|
||||
return (
|
||||
<>
|
||||
{nodeInfo?.beeMode !== BeeModes.ULTRA_LIGHT && <FileNavigation active="DOWNLOAD" />}
|
||||
{nodeInfo?.beeMode !== BeeModes.ULTRA_LIGHT && <FileNavigation active={FileOrigin.Download} />}
|
||||
<ExpandableListItemInput
|
||||
label="Swarm Hash or ENS"
|
||||
onConfirm={value => onSwarmIdentifier(value)}
|
||||
@@ -97,7 +90,7 @@ export function Download(): ReactElement {
|
||||
mapperFn={value => recognizeEnsOrSwarmHash(value)}
|
||||
loading={loading}
|
||||
/>
|
||||
<History title="Download History" localStorageKey={HISTORY_KEYS.DOWNLOAD_HISTORY} />
|
||||
<History title="Download History" localStorageKey={LocalStorageKeys.downloadHistory} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Box, Grid } from '@material-ui/core'
|
||||
import { Box, Grid } from '@mui/material'
|
||||
import { ReactElement } from 'react'
|
||||
import X from 'remixicon-react/CloseLineIcon'
|
||||
import Bookmark from 'remixicon-react/BookmarkLineIcon'
|
||||
import X from 'remixicon-react/CloseLineIcon'
|
||||
import Download from 'remixicon-react/DownloadLineIcon'
|
||||
import Link from 'remixicon-react/LinkIcon'
|
||||
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
|
||||
|
||||
@@ -1,40 +1,46 @@
|
||||
import { createStyles, makeStyles, Tab, Tabs, Theme } from '@material-ui/core'
|
||||
import { ReactElement } from 'react'
|
||||
import { Tab, Tabs } from '@mui/material'
|
||||
import React, { ReactElement } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { makeStyles } from 'tss-react/mui'
|
||||
|
||||
import { ROUTES } from '../../routes'
|
||||
|
||||
interface Props {
|
||||
active: 'UPLOAD' | 'DOWNLOAD'
|
||||
export enum FileOrigin {
|
||||
Upload = 'UPLOAD',
|
||||
Download = 'DOWNLOAD',
|
||||
Feed = 'FEED',
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
marginBottom: theme.spacing(4),
|
||||
},
|
||||
leftTab: {
|
||||
marginRight: theme.spacing(0.5),
|
||||
},
|
||||
rightTab: {
|
||||
marginLeft: theme.spacing(0.5),
|
||||
},
|
||||
}),
|
||||
)
|
||||
interface Props {
|
||||
active: FileOrigin
|
||||
}
|
||||
|
||||
const useStyles = makeStyles()(theme => ({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
marginBottom: theme.spacing(4),
|
||||
},
|
||||
leftTab: {
|
||||
marginRight: theme.spacing(0.5),
|
||||
},
|
||||
rightTab: {
|
||||
marginLeft: theme.spacing(0.5),
|
||||
},
|
||||
}))
|
||||
|
||||
export function FileNavigation({ active }: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
const { classes } = useStyles()
|
||||
const navigate = useNavigate()
|
||||
|
||||
function onChange(event: React.ChangeEvent<Record<string, never>>, newValue: number) {
|
||||
function onChange(_event: React.SyntheticEvent, newValue: number) {
|
||||
navigate(newValue === 1 ? ROUTES.DOWNLOAD : ROUTES.UPLOAD)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Tabs value={active === 'UPLOAD' ? 0 : 1} onChange={onChange} variant="fullWidth">
|
||||
<Tab className={classes.leftTab} key="UPLOAD" label="Upload" />
|
||||
<Tab className={classes.rightTab} key="DOWNLOAD" label="Download" />
|
||||
<Tabs value={active === FileOrigin.Upload ? 0 : 1} onChange={onChange} variant="fullWidth">
|
||||
<Tab className={classes.leftTab} key={FileOrigin.Upload} label="Upload" />
|
||||
<Tab className={classes.rightTab} key={FileOrigin.Download} label="Download" />
|
||||
</Tabs>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Button, ListItemIcon, Menu, MenuItem, Typography } from '@material-ui/core'
|
||||
import { Button, ListItemIcon, Menu, MenuItem, Typography } from '@mui/material'
|
||||
import React, { ReactElement } from 'react'
|
||||
|
||||
import { EnrichedPostageBatch } from '../../providers/Stamps'
|
||||
|
||||
interface Props {
|
||||
|
||||
+33
-30
@@ -1,10 +1,11 @@
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { Bytes, MantarayNode, NULL_ADDRESS } from '@ethersphere/bee-js'
|
||||
import { Bytes } from '@ethersphere/bee-js'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { saveAs } from 'file-saver'
|
||||
import JSZip from 'jszip'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useRef, useState } from 'react'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
import { Loading } from '../../components/Loading'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
@@ -12,7 +13,9 @@ import { META_FILE_NAME } from '../../constants'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { determineHistoryName, HISTORY_KEYS, putHistory } from '../../utils/local-storage'
|
||||
import { determineHistoryName, LocalStorageKeys, putHistory } from '../../utils/localStorage'
|
||||
import { loadManifest } from '../../utils/manifest'
|
||||
|
||||
import { AssetPreview } from './AssetPreview'
|
||||
import { AssetSummary } from './AssetSummary'
|
||||
import { AssetSyncing } from './AssetSyncing'
|
||||
@@ -23,7 +26,6 @@ export function Share(): ReactElement {
|
||||
const { status } = useContext(BeeContext)
|
||||
|
||||
const { hash } = useParams()
|
||||
const reference = hash! // eslint-disable-line
|
||||
|
||||
const navigate = useNavigate()
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
@@ -39,23 +41,12 @@ export function Share(): ReactElement {
|
||||
const isMountedRef = useRef(true)
|
||||
|
||||
async function prepare() {
|
||||
if (!beeApi || !status.all) {
|
||||
if (!beeApi || !status.all || !hash) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
let manifest = await MantarayNode.unmarshal(beeApi, reference)
|
||||
await manifest.loadRecursively(beeApi)
|
||||
|
||||
// If the manifest is a feed, resolve it and overwrite the manifest
|
||||
await manifest.resolveFeed(beeApi).then(
|
||||
async feed =>
|
||||
await feed.ifPresentAsync(async feedUpdate => {
|
||||
manifest = MantarayNode.unmarshalFromData(feedUpdate.payload.toUint8Array(), NULL_ADDRESS)
|
||||
await manifest.loadRecursively(beeApi)
|
||||
}),
|
||||
)
|
||||
|
||||
const manifest = await loadManifest(beeApi, hash)
|
||||
const entries = manifest.collectAndMap()
|
||||
delete entries[META_FILE_NAME]
|
||||
|
||||
@@ -73,18 +64,18 @@ export function Share(): ReactElement {
|
||||
setIndexDocument(indexDocument)
|
||||
|
||||
try {
|
||||
const remoteMetadata = await beeApi.downloadFile(reference, META_FILE_NAME)
|
||||
const remoteMetadata = await beeApi.downloadFile(hash, META_FILE_NAME)
|
||||
const formattedMetadata = remoteMetadata.data.toJSON() as Metadata
|
||||
|
||||
if (formattedMetadata.isVideo || formattedMetadata.isAudio || formattedMetadata.isImage) {
|
||||
if (!isMountedRef.current) return
|
||||
setPreview(`${apiUrl}/bzz/${reference}`)
|
||||
setPreview(`${apiUrl}/bzz/${hash}`)
|
||||
}
|
||||
|
||||
if (!isMountedRef.current) return
|
||||
|
||||
setMetadata({ ...formattedMetadata, hash })
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// if metadata is not available or invalid go with the default one
|
||||
const count = Object.keys(entries).length
|
||||
|
||||
@@ -93,7 +84,7 @@ export function Share(): ReactElement {
|
||||
setMetadata({
|
||||
hash,
|
||||
type: count > 1 ? 'folder' : 'unknown',
|
||||
name: reference,
|
||||
name: hash,
|
||||
count,
|
||||
isWebsite: Boolean(indexDocument && /.*\.html?$/i.test(indexDocument)),
|
||||
isVideo: Boolean(indexDocument && /.*\.(mp4|webm|ogv)$/i.test(indexDocument)),
|
||||
@@ -113,7 +104,7 @@ export function Share(): ReactElement {
|
||||
}
|
||||
|
||||
function onOpen() {
|
||||
window.open(`${apiUrl}/bzz/${reference}/`, '_blank')
|
||||
window.open(`${apiUrl}/bzz/${hash}/`, '_blank')
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
@@ -127,10 +118,19 @@ export function Share(): ReactElement {
|
||||
}
|
||||
|
||||
function onUpdateFeed() {
|
||||
navigate(ROUTES.ACCOUNT_FEEDS_UPDATE.replace(':hash', reference))
|
||||
if (!hash) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('hash is invalid')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
navigate(ROUTES.ACCOUNT_FEEDS_UPDATE.replace(':hash', hash))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
isMountedRef.current = true
|
||||
|
||||
return () => {
|
||||
isMountedRef.current = false
|
||||
}
|
||||
@@ -143,14 +143,17 @@ export function Share(): ReactElement {
|
||||
setLoading(false)
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [reference])
|
||||
}, [hash])
|
||||
|
||||
async function onDownload() {
|
||||
if (!beeApi) {
|
||||
if (!beeApi || !hash) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('hash is invalid')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
putHistory(HISTORY_KEYS.DOWNLOAD_HISTORY, reference, determineHistoryName(reference, indexDocument))
|
||||
putHistory(LocalStorageKeys.downloadHistory, hash, determineHistoryName(hash, indexDocument))
|
||||
setDownloading(true)
|
||||
|
||||
if (Object.keys(swarmEntries).length === 1) {
|
||||
@@ -172,7 +175,7 @@ export function Share(): ReactElement {
|
||||
const view = new Uint8Array(arrayBuffer)
|
||||
view.set(dataArray)
|
||||
const blob = new Blob([arrayBuffer], { type: metadata?.type || 'application/octet-stream' })
|
||||
saveAs(blob, metadata?.name || singleFileName || reference)
|
||||
saveAs(blob, metadata?.name || singleFileName || hash)
|
||||
} else {
|
||||
const zip = new JSZip()
|
||||
for (const [path, hash] of Object.entries(swarmEntries)) {
|
||||
@@ -196,7 +199,7 @@ export function Share(): ReactElement {
|
||||
return
|
||||
}
|
||||
|
||||
saveAs(content, reference + '.zip')
|
||||
saveAs(content, hash + '.zip')
|
||||
}
|
||||
|
||||
if (!isMountedRef.current) return
|
||||
@@ -225,10 +228,10 @@ export function Share(): ReactElement {
|
||||
<AssetPreview metadata={metadata} previewUri={preview} />
|
||||
</Box>
|
||||
<Box mb={4}>
|
||||
<AssetSummary isWebsite={metadata?.isWebsite} reference={reference} />
|
||||
<AssetSummary isWebsite={metadata?.isWebsite} reference={hash} />
|
||||
</Box>
|
||||
<Box mb={4}>
|
||||
<AssetSyncing reference={reference} />
|
||||
<AssetSyncing reference={hash} />
|
||||
</Box>
|
||||
<DownloadActionBar
|
||||
onOpen={onOpen}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
import { EnrichedPostageBatch } from '../../providers/Stamps'
|
||||
import { PostageStamp } from '../stamps/PostageStamp'
|
||||
|
||||
|
||||
+19
-15
@@ -1,32 +1,35 @@
|
||||
import { Box } from '@material-ui/core'
|
||||
import { Box } from '@mui/material'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { DocumentationText } from '../../components/DocumentationText'
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
import { ProgressIndicator } from '../../components/ProgressIndicator'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { META_FILE_NAME } from '../../constants'
|
||||
import { Context as BeeContext, CheckState } from '../../providers/Bee'
|
||||
import { Identity, Context as IdentityContext } from '../../providers/Feeds'
|
||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as IdentityContext, Identity, IdentityType } from '../../providers/Feeds'
|
||||
import { Context as FileContext } from '../../providers/File'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { EnrichedPostageBatch, Context as StampsContext } from '../../providers/Stamps'
|
||||
import { Context as StampsContext, EnrichedPostageBatch } from '../../providers/Stamps'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { waitUntilStampUsable } from '../../utils'
|
||||
import { detectIndexHtml, getAssetNameFromFiles, packageFile } from '../../utils/file'
|
||||
import { persistIdentity, updateFeed } from '../../utils/identity'
|
||||
import { HISTORY_KEYS, putHistory } from '../../utils/local-storage'
|
||||
import { LocalStorageKeys, putHistory } from '../../utils/localStorage'
|
||||
import { FeedPasswordDialog } from '../feeds/FeedPasswordDialog'
|
||||
import { PostageStampAdvancedCreation } from '../stamps/PostageStampAdvancedCreation'
|
||||
import { PostageStampSelector } from '../stamps/PostageStampSelector'
|
||||
|
||||
import { AssetPreview } from './AssetPreview'
|
||||
import { FileOrigin } from './FileNavigation'
|
||||
import { StampPreview } from './StampPreview'
|
||||
import { UploadActionBar } from './UploadActionBar'
|
||||
import { StampMode, UploadActionBar } from './UploadActionBar'
|
||||
|
||||
export function Upload(): ReactElement {
|
||||
const [step, setStep] = useState(0)
|
||||
const [stampMode, setStampMode] = useState<'SELECT' | 'BUY'>('SELECT')
|
||||
const [stampMode, setStampMode] = useState<StampMode>(StampMode.Select)
|
||||
const [stamp, setStamp] = useState<EnrichedPostageBatch | null>(null)
|
||||
const [isUploading, setUploading] = useState(false)
|
||||
const [showPasswordPrompt, setShowPasswordPrompt] = useState(false)
|
||||
@@ -44,7 +47,7 @@ export function Upload(): ReactElement {
|
||||
|
||||
useEffect(() => {
|
||||
refresh()
|
||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, [refresh])
|
||||
|
||||
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
|
||||
|
||||
@@ -58,10 +61,10 @@ export function Upload(): ReactElement {
|
||||
const identity = uploadOrigin.uuid ? identities.find(x => x.uuid === uploadOrigin.uuid) : null
|
||||
|
||||
const onUpload = () => {
|
||||
if (uploadOrigin.origin === 'UPLOAD') {
|
||||
if (uploadOrigin.origin === FileOrigin.Upload) {
|
||||
uploadFiles()
|
||||
} else {
|
||||
if ((identity as Identity).type === 'PRIVATE_KEY') {
|
||||
if ((identity as Identity).type === IdentityType.PrivateKey) {
|
||||
uploadFiles()
|
||||
} else {
|
||||
setShowPasswordPrompt(true)
|
||||
@@ -114,9 +117,9 @@ export function Upload(): ReactElement {
|
||||
beeApi
|
||||
.uploadFiles(stamp.batchID, fls, { indexDocument, deferred: true })
|
||||
.then(hash => {
|
||||
putHistory(HISTORY_KEYS.UPLOAD_HISTORY, hash.reference.toHex(), getAssetNameFromFiles(files))
|
||||
putHistory(LocalStorageKeys.uploadHistory, hash.reference.toHex(), getAssetNameFromFiles(files))
|
||||
|
||||
if (uploadOrigin.origin === 'UPLOAD') {
|
||||
if (uploadOrigin.origin === FileOrigin.Upload) {
|
||||
navigate(ROUTES.HASH.replace(':hash', hash.reference.toHex()), { replace: true })
|
||||
} else {
|
||||
updateFeed(beeApi, identity as Identity, hash.reference, stamp.batchID, password as string).then(() => {
|
||||
@@ -127,7 +130,8 @@ export function Upload(): ReactElement {
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e) // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e)
|
||||
enqueueSnackbar(`Error uploading: ${e.message}`, { variant: 'error' })
|
||||
setUploading(false)
|
||||
})
|
||||
@@ -163,10 +167,10 @@ export function Upload(): ReactElement {
|
||||
{step === 1 && (
|
||||
<>
|
||||
<Box mb={2}>
|
||||
{hasAnyStamps && stampMode === 'SELECT' ? (
|
||||
{hasAnyStamps && stampMode === StampMode.Select ? (
|
||||
<PostageStampSelector onSelect={stamp => setStamp(stamp)} defaultValue={stamp?.batchID.toHex()} />
|
||||
) : (
|
||||
<PostageStampAdvancedCreation onFinished={() => setStampMode('SELECT')} />
|
||||
<PostageStampAdvancedCreation onFinished={() => setStampMode(StampMode.Select)} />
|
||||
)}
|
||||
</Box>
|
||||
<Box mb={4}>
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import { Box, Grid } from '@material-ui/core'
|
||||
import { Box, Grid } from '@mui/material'
|
||||
import { ReactElement } from 'react'
|
||||
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
|
||||
import ArrowLeft from 'remixicon-react/ArrowLeftLineIcon'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
import X from 'remixicon-react/CloseLineIcon'
|
||||
import ArrowLeft from 'remixicon-react/ArrowLeftLineIcon'
|
||||
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
|
||||
import Layers from 'remixicon-react/StackLineIcon'
|
||||
|
||||
import { DocumentationText } from '../../components/DocumentationText'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
|
||||
export enum StampMode {
|
||||
Buy = 'BUY',
|
||||
Select = 'SELECT',
|
||||
}
|
||||
|
||||
interface Props {
|
||||
step: number
|
||||
onUpload: () => void
|
||||
@@ -19,8 +25,8 @@ interface Props {
|
||||
hasStamp: boolean
|
||||
hasAnyStamps: boolean
|
||||
uploadLabel: string
|
||||
stampMode: 'BUY' | 'SELECT'
|
||||
setStampMode: (mode: 'BUY' | 'SELECT') => void
|
||||
stampMode: StampMode
|
||||
setStampMode: (mode: StampMode) => void
|
||||
}
|
||||
|
||||
export function UploadActionBar({
|
||||
@@ -68,7 +74,7 @@ export function UploadActionBar({
|
||||
return (
|
||||
<Grid container direction="row" justifyContent="space-between">
|
||||
<ExpandableListItemActions>
|
||||
{stampMode === 'SELECT' && (
|
||||
{stampMode === StampMode.Select && (
|
||||
<SwarmButton onClick={onProceed} iconType={Check} disabled={!hasStamp}>
|
||||
Proceed With Selected Stamp
|
||||
</SwarmButton>
|
||||
@@ -79,11 +85,11 @@ export function UploadActionBar({
|
||||
</ExpandableListItemActions>
|
||||
{hasAnyStamps && (
|
||||
<SwarmButton
|
||||
disabled={stampMode === 'BUY' && !hasAnyStamps}
|
||||
onClick={() => setStampMode(stampMode === 'BUY' ? 'SELECT' : 'BUY')}
|
||||
iconType={stampMode === 'BUY' ? Layers : PlusSquare}
|
||||
disabled={stampMode === StampMode.Buy && !hasAnyStamps}
|
||||
onClick={() => setStampMode(stampMode === StampMode.Buy ? StampMode.Select : StampMode.Buy)}
|
||||
iconType={stampMode === StampMode.Buy ? Layers : PlusSquare}
|
||||
>
|
||||
{stampMode === 'BUY' ? 'Use Existing Stamp' : 'Buy New Stamp'}
|
||||
{stampMode === StampMode.Buy ? 'Use Existing Stamp' : 'Buy New Stamp'}
|
||||
</SwarmButton>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
@@ -1,75 +1,83 @@
|
||||
import { createStyles, makeStyles, Theme } from '@material-ui/core'
|
||||
import { DropzoneArea } from 'material-ui-dropzone'
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { Box } from '@mui/material'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { ReactElement, useContext, useRef, useState } from 'react'
|
||||
import { useDropzone } from 'react-dropzone'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import PlusCircle from 'remixicon-react/AddCircleLineIcon'
|
||||
import FilePlus from 'remixicon-react/FileAddLineIcon'
|
||||
import FolderPlus from 'remixicon-react/FolderAddLineIcon'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { makeStyles } from 'tss-react/mui'
|
||||
|
||||
import { DocumentationText } from '../../components/DocumentationText'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { Context, UploadOrigin } from '../../providers/File'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context, UploadOrigin } from '../../providers/File'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { detectIndexHtml } from '../../utils/file'
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
|
||||
interface Props {
|
||||
uploadOrigin: UploadOrigin
|
||||
showHelp: boolean
|
||||
}
|
||||
|
||||
const MAX_FILE_SIZE = 1_000_000_000 // 1 gigabyte
|
||||
const MAX_FILE_SIZE = 1_000_000_000 // 1 GB
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
areaWrapper: { position: 'relative', marginBottom: theme.spacing(2) },
|
||||
dropzone: {
|
||||
background: theme.palette.background.default,
|
||||
outline: 'none',
|
||||
color: 'transparent',
|
||||
zIndex: 1,
|
||||
'& svg': {
|
||||
opacity: 0,
|
||||
},
|
||||
const useStyles = makeStyles()(theme => ({
|
||||
areaWrapper: { position: 'relative', marginBottom: theme.spacing(2) },
|
||||
dropzone: {
|
||||
background: theme.palette.background.default,
|
||||
outline: 'none',
|
||||
border: '2px dashed #ccc',
|
||||
borderRadius: 4,
|
||||
padding: theme.spacing(4),
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
minHeight: 200,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
'&:hover': {
|
||||
borderColor: theme.palette.primary.main,
|
||||
},
|
||||
buttonWrapper: {
|
||||
top: '0',
|
||||
left: '0',
|
||||
position: 'absolute',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
button: {
|
||||
marginLeft: theme.spacing(0.5),
|
||||
marginRight: theme.spacing(0.5),
|
||||
zIndex: 2,
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
buttonWrapper: {
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(1),
|
||||
},
|
||||
button: {
|
||||
zIndex: 2,
|
||||
},
|
||||
}))
|
||||
|
||||
export function UploadArea({ uploadOrigin, showHelp }: Props): ReactElement {
|
||||
const { setFiles, setUploadOrigin } = useContext(Context)
|
||||
const { nodeInfo } = useContext(BeeContext)
|
||||
const classes = useStyles()
|
||||
const { classes } = useStyles()
|
||||
const navigate = useNavigate()
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
const [strictWebsiteMode, setStrictWebsiteMode] = useState(false)
|
||||
const [version, setVersion] = useState(0)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
const getDropzoneInputDomElement = () => document.querySelector('.MuiDropzoneArea-root input') as HTMLInputElement
|
||||
const onDrop = (acceptedFiles: File[]) => {
|
||||
handleChange(acceptedFiles)
|
||||
}
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
onDrop,
|
||||
maxSize: MAX_FILE_SIZE,
|
||||
noClick: true,
|
||||
})
|
||||
|
||||
const onUploadCollectionClick = () => {
|
||||
const element = getDropzoneInputDomElement()
|
||||
|
||||
if (element) {
|
||||
element.setAttribute('directory', '')
|
||||
element.setAttribute('webkitdirectory', '')
|
||||
element.setAttribute('mozdirectory', '')
|
||||
element.click()
|
||||
if (inputRef.current) {
|
||||
inputRef.current.setAttribute('directory', '')
|
||||
inputRef.current.setAttribute('webkitdirectory', '')
|
||||
inputRef.current.setAttribute('mozdirectory', '')
|
||||
inputRef.current.click()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,23 +92,14 @@ export function UploadArea({ uploadOrigin, showHelp }: Props): ReactElement {
|
||||
}
|
||||
|
||||
const onUploadFileClick = () => {
|
||||
const element = getDropzoneInputDomElement()
|
||||
|
||||
if (element) {
|
||||
element.removeAttribute('directory')
|
||||
element.removeAttribute('webkitdirectory')
|
||||
element.removeAttribute('mozdirectory')
|
||||
element.click()
|
||||
if (inputRef.current) {
|
||||
inputRef.current.removeAttribute('directory')
|
||||
inputRef.current.removeAttribute('webkitdirectory')
|
||||
inputRef.current.removeAttribute('mozdirectory')
|
||||
inputRef.current.click()
|
||||
}
|
||||
}
|
||||
|
||||
const resetComponentOnAddingInvalidContent = () => {
|
||||
setTimeout(() => {
|
||||
setVersion(x => x + 1)
|
||||
setFiles([])
|
||||
}, 0)
|
||||
}
|
||||
|
||||
const handleChange = (files?: File[]) => {
|
||||
if (files) {
|
||||
const FilePaths = files as FilePath[]
|
||||
@@ -110,7 +109,8 @@ export function UploadArea({ uploadOrigin, showHelp }: Props): ReactElement {
|
||||
enqueueSnackbar('To upload a website, there must be an index.html or index.htm in the root of the folder.', {
|
||||
variant: 'error',
|
||||
})
|
||||
resetComponentOnAddingInvalidContent()
|
||||
setFiles([])
|
||||
setStrictWebsiteMode(false)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -129,27 +129,22 @@ export function UploadArea({ uploadOrigin, showHelp }: Props): ReactElement {
|
||||
return (
|
||||
<>
|
||||
{isUploadEnabled && (
|
||||
<div className={classes.areaWrapper}>
|
||||
<DropzoneArea
|
||||
key={version}
|
||||
dropzoneClass={classes.dropzone}
|
||||
onChange={handleChange}
|
||||
filesLimit={1e9}
|
||||
maxFileSize={MAX_FILE_SIZE}
|
||||
showPreviews={false}
|
||||
/>
|
||||
<div className={classes.buttonWrapper}>
|
||||
<SwarmButton className={classes.button} onClick={onUploadFileClick} iconType={FilePlus}>
|
||||
Add File
|
||||
</SwarmButton>
|
||||
<SwarmButton className={classes.button} onClick={onUploadFolderClick} iconType={FolderPlus}>
|
||||
Add Folder
|
||||
</SwarmButton>
|
||||
<SwarmButton className={classes.button} onClick={onUploadWebsiteClick} iconType={PlusCircle}>
|
||||
Add Website
|
||||
</SwarmButton>
|
||||
</div>
|
||||
</div>
|
||||
<Box className={classes.areaWrapper}>
|
||||
<Box {...getRootProps()} className={classes.dropzone}>
|
||||
<input {...getInputProps()} ref={inputRef} />
|
||||
<Box className={classes.buttonWrapper}>
|
||||
<SwarmButton className={classes.button} onClick={onUploadFileClick} iconType={FilePlus}>
|
||||
Add File
|
||||
</SwarmButton>
|
||||
<SwarmButton className={classes.button} onClick={onUploadFolderClick} iconType={FolderPlus}>
|
||||
Add Folder
|
||||
</SwarmButton>
|
||||
<SwarmButton className={classes.button} onClick={onUploadWebsiteClick} iconType={PlusCircle}>
|
||||
Add Website
|
||||
</SwarmButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
{isUploadEnabled && showHelp && (
|
||||
<DocumentationText>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { ReactElement, useContext } from 'react'
|
||||
|
||||
import { History } from '../../components/History'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
||||
import { defaultUploadOrigin } from '../../providers/File'
|
||||
import { HISTORY_KEYS } from '../../utils/local-storage'
|
||||
import { FileNavigation } from './FileNavigation'
|
||||
import { LocalStorageKeys } from '../../utils/localStorage'
|
||||
|
||||
import { FileNavigation, FileOrigin } from './FileNavigation'
|
||||
import { UploadArea } from './UploadArea'
|
||||
|
||||
export function UploadLander(): ReactElement {
|
||||
@@ -14,9 +16,9 @@ export function UploadLander(): ReactElement {
|
||||
|
||||
return (
|
||||
<>
|
||||
<FileNavigation active="UPLOAD" />
|
||||
<FileNavigation active={FileOrigin.Upload} />
|
||||
<UploadArea showHelp={true} uploadOrigin={defaultUploadOrigin} />
|
||||
<History title="Upload History" localStorageKey={HISTORY_KEYS.UPLOAD_HISTORY} />
|
||||
<History title="Upload History" localStorageKey={LocalStorageKeys.uploadHistory} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { BZZ, DAI } from '@ethersphere/bee-js'
|
||||
import { Box, Tooltip, Typography } from '@material-ui/core'
|
||||
import { Wallet } from 'ethers'
|
||||
import { Box, Tooltip, Typography } from '@mui/material'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
import X from 'remixicon-react/CloseLineIcon'
|
||||
|
||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
@@ -16,6 +16,7 @@ import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context as TopUpContext } from '../../providers/TopUp'
|
||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||
import { createGiftWallet } from '../../utils/desktop'
|
||||
import { generateWallet } from '../../utils/identity'
|
||||
import { ResolvedWallet } from '../../utils/wallet'
|
||||
|
||||
const GIFT_WALLET_FUND_DAI_AMOUNT = DAI.fromDecimalString('0.1')
|
||||
@@ -51,12 +52,13 @@ export default function Index(): ReactElement {
|
||||
enqueueSnackbar('Sending funds to gift wallet...')
|
||||
setLoading(true)
|
||||
try {
|
||||
const wallet = Wallet.createRandom()
|
||||
const wallet = generateWallet()
|
||||
addGiftWallet(wallet)
|
||||
await createGiftWallet(desktopUrl, wallet.address)
|
||||
enqueueSnackbar('Succesfully funded gift wallet', { variant: 'success' })
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error)
|
||||
enqueueSnackbar(`Failed to fund gift wallet: ${error}`, { variant: 'error' })
|
||||
} finally {
|
||||
setLoading(false)
|
||||
@@ -2,8 +2,9 @@ import { BZZ } from '@ethersphere/bee-js'
|
||||
import { useContext } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import ExchangeFunds from 'remixicon-react/ExchangeFundsLineIcon'
|
||||
|
||||
import Card from '../../components/Card'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
||||
import { ROUTES } from '../../routes'
|
||||
|
||||
export function ChequebookInfoCard() {
|
||||
@@ -21,7 +22,7 @@ export function ChequebookInfoCard() {
|
||||
icon={<ExchangeFunds />}
|
||||
title={`${chequebookBalance?.availableBalance.toSignificantDigits(4)} xBZZ`}
|
||||
subtitle="Network transfer balance."
|
||||
status="ok"
|
||||
status={CheckState.OK}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -40,7 +41,7 @@ export function ChequebookInfoCard() {
|
||||
: 'No available balance.'
|
||||
}
|
||||
subtitle="Chequebook not setup."
|
||||
status="error"
|
||||
status={CheckState.ERROR}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import Globe from 'remixicon-react/GlobalLineIcon'
|
||||
import Search from 'remixicon-react/SearchLineIcon'
|
||||
import Settings from 'remixicon-react/Settings2LineIcon'
|
||||
|
||||
import { useNavigate } from 'react-router'
|
||||
import Card from '../../components/Card'
|
||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
||||
import { ROUTES } from '../../routes'
|
||||
@@ -19,7 +19,7 @@ export default function NodeInfoCard(): ReactElement {
|
||||
icon={<Globe />}
|
||||
title="Connecting..."
|
||||
subtitle="Attempting to establish connection to your Bee node."
|
||||
status="connecting"
|
||||
status={CheckState.CONNECTING}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -31,7 +31,7 @@ export default function NodeInfoCard(): ReactElement {
|
||||
icon={<Globe />}
|
||||
title="Starting up..."
|
||||
subtitle="Your Bee node is currently launching."
|
||||
status="loading"
|
||||
status={CheckState.STARTING}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -43,7 +43,7 @@ export default function NodeInfoCard(): ReactElement {
|
||||
icon={<Globe />}
|
||||
title="Your node is not connected…"
|
||||
subtitle="You are not connected to Swarm."
|
||||
status="error"
|
||||
status={CheckState.ERROR}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export default function NodeInfoCard(): ReactElement {
|
||||
icon={<Globe />}
|
||||
title="Your node is running…"
|
||||
subtitle="Connection to Swarm might not be optimal."
|
||||
status="error"
|
||||
status={CheckState.WARNING}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -66,7 +66,7 @@ export default function NodeInfoCard(): ReactElement {
|
||||
icon={<Globe />}
|
||||
title="Your node is connected."
|
||||
subtitle="You are connected to Swarm."
|
||||
status="ok"
|
||||
status={CheckState.OK}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ import { useContext } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import Upload from 'remixicon-react/UploadLineIcon'
|
||||
import Wallet from 'remixicon-react/Wallet3LineIcon'
|
||||
|
||||
import Card from '../../components/Card'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
||||
import { ROUTES } from '../../routes'
|
||||
|
||||
export function WalletInfoCard() {
|
||||
@@ -29,7 +30,7 @@ export function WalletInfoCard() {
|
||||
icon={<Wallet />}
|
||||
title={balanceText}
|
||||
subtitle="Current wallet balance."
|
||||
status="ok"
|
||||
status={CheckState.OK}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -44,7 +45,7 @@ export function WalletInfoCard() {
|
||||
icon={<Upload />}
|
||||
title="Your wallet is not setup."
|
||||
subtitle="To share content on Swarm, please setup your wallet."
|
||||
status="error"
|
||||
status={CheckState.ERROR}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Button } from '@material-ui/core'
|
||||
import { Button } from '@mui/material'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
|
||||
import { ChainSync } from '../../components/ChainSync'
|
||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||
import Map from '../../components/Map'
|
||||
@@ -8,6 +9,7 @@ import { useBeeDesktop, useNewBeeDesktopVersion } from '../../hooks/apiHooks'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { chainIdToName } from '../../utils/chain'
|
||||
|
||||
import { ChequebookInfoCard } from './ChequebookInfoCard'
|
||||
import NodeInfoCard from './NodeInfoCard'
|
||||
import { WalletInfoCard } from './WalletInfoCard'
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Typography } from '@mui/material'
|
||||
import { ReactElement } from 'react'
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
import { Typography } from '@material-ui/core'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
import { ROUTES } from '../../routes'
|
||||
|
||||
export default function PageNotFound(): ReactElement {
|
||||
@@ -1,13 +1,16 @@
|
||||
import { Box, Grid, Typography } from '@material-ui/core'
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { Box, Grid, Typography } from '@mui/material'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
|
||||
import { ChainSync } from '../../components/ChainSync'
|
||||
import { Waiting } from '../../components/Waiting'
|
||||
import { Context } from '../../providers/Settings'
|
||||
import { ROUTES } from '../../routes'
|
||||
|
||||
const LIGHTMODE_START_INTERVAL_MS = 3_000
|
||||
|
||||
export default function LightModeRestart(): ReactElement {
|
||||
const navigate = useNavigate()
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
@@ -27,8 +30,9 @@ export default function LightModeRestart(): ReactElement {
|
||||
navigate(ROUTES.INFO)
|
||||
}
|
||||
})
|
||||
.catch(console.error) // eslint-disable-line
|
||||
}, 3_000)
|
||||
// eslint-disable-next-line no-console
|
||||
.catch(console.error)
|
||||
}, LIGHTMODE_START_INTERVAL_MS)
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [beeApi, enqueueSnackbar, navigate])
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||
import CircularProgress from '@mui/material/CircularProgress'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
|
||||
import ExpandableList from '../../components/ExpandableList'
|
||||
import ExpandableListItemInput from '../../components/ExpandableListItemInput'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
@@ -44,7 +45,8 @@ export default function SettingsPage(): ReactElement {
|
||||
|
||||
await refresh()
|
||||
} catch (e) {
|
||||
console.error(e) //eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e)
|
||||
enqueueSnackbar(`Failed to change RPC endpoint. ${e}`, { variant: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { ReactElement } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
import { ROUTES } from '../../routes'
|
||||
|
||||
import { PostageStampAdvancedCreation } from './PostageStampAdvancedCreation'
|
||||
|
||||
export function CreatePostageStampPage(): ReactElement {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
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 {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Box, Grid, Typography } from '@material-ui/core'
|
||||
import { Box, Grid, Typography } from '@mui/material'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
import { Capacity } from '../../components/Capacity'
|
||||
import { EnrichedPostageBatch } from '../../providers/Stamps'
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Box, Grid, IconButton, Typography, createStyles, makeStyles } from '@material-ui/core'
|
||||
import { PostageBatchOptions, Utils } from '@ethersphere/bee-js'
|
||||
import { Box, Grid, IconButton, Typography } from '@mui/material'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
import Info from 'remixicon-react/InformationLineIcon'
|
||||
import { makeStyles } from 'tss-react/mui'
|
||||
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { SwarmSelect } from '../../components/SwarmSelect'
|
||||
import { SwarmTextInput } from '../../components/SwarmTextInput'
|
||||
@@ -20,33 +22,31 @@ interface Props {
|
||||
onFinished: () => void
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
link: {
|
||||
color: '#dd7700',
|
||||
textDecoration: 'underline',
|
||||
'&:hover': {
|
||||
textDecoration: 'none',
|
||||
const useStyles = makeStyles()(() => ({
|
||||
link: {
|
||||
color: '#dd7700',
|
||||
textDecoration: 'underline',
|
||||
'&:hover': {
|
||||
textDecoration: 'none',
|
||||
|
||||
// https://github.com/mui-org/material-ui/issues/22543
|
||||
'@media (hover: none)': {
|
||||
textDecoration: 'none',
|
||||
},
|
||||
// https://github.com/mui-org/material-ui/issues/22543
|
||||
'@media (hover: none)': {
|
||||
textDecoration: 'none',
|
||||
},
|
||||
},
|
||||
stampVolumeWrapper: {
|
||||
width: 'fit-content',
|
||||
'& button': {
|
||||
marginLeft: 4,
|
||||
width: 24,
|
||||
padding: 2,
|
||||
},
|
||||
},
|
||||
stampVolumeWrapper: {
|
||||
width: 'fit-content',
|
||||
'& button': {
|
||||
marginLeft: 4,
|
||||
width: 24,
|
||||
padding: 2,
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
}))
|
||||
|
||||
export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
const { classes } = useStyles()
|
||||
const { chainState } = useContext(BeeContext)
|
||||
const { refresh } = useContext(StampsContext)
|
||||
const { beeApi } = useContext(SettingsContext)
|
||||
@@ -115,7 +115,8 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
||||
await refresh()
|
||||
onFinished()
|
||||
} catch (e) {
|
||||
console.error(e) // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e)
|
||||
enqueueSnackbar(`Error: ${(e as Error).message}`, { variant: 'error' })
|
||||
}
|
||||
setSubmitting(false)
|
||||
@@ -180,7 +181,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
||||
const effectiveVolume = getHumanReadableFileSize(Utils.getStampEffectiveBytes(depth))
|
||||
|
||||
return (
|
||||
<Grid item container alignItems="center" className={classes.stampVolumeWrapper}>
|
||||
<Grid container alignItems="center" className={classes.stampVolumeWrapper}>
|
||||
<Typography>
|
||||
Theoretical: ~{theoreticalMaximumVolume} / Effective: ~{effectiveVolume}
|
||||
</Typography>
|
||||
@@ -241,7 +242,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
||||
<Box mb={2}>
|
||||
<SwarmSelect
|
||||
label="Immutable"
|
||||
defaultValue="No"
|
||||
value="No"
|
||||
onChange={event => setImmutable(event.target.value === 'Yes')}
|
||||
options={[
|
||||
{ value: 'Yes', label: 'Yes' },
|
||||
@@ -277,7 +278,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
||||
</Box>
|
||||
|
||||
<Grid container justifyContent="space-between" alignItems="center">
|
||||
<Grid item>
|
||||
<Grid>
|
||||
<SwarmButton
|
||||
disabled={submitting || Boolean(depthError) || Boolean(amountError) || !depthInput || !amountInput}
|
||||
onClick={submit}
|
||||
@@ -287,7 +288,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
||||
Buy New Stamp
|
||||
</SwarmButton>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Grid>
|
||||
<Link to={ROUTES.ACCOUNT_STAMPS_NEW_STANDARD} className={classes.link}>
|
||||
Standard mode
|
||||
</Link>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
|
||||
import { SwarmSelect } from '../../components/SwarmSelect'
|
||||
import { Context, EnrichedPostageBatch } from '../../providers/Stamps'
|
||||
|
||||
@@ -9,11 +10,15 @@ interface Props {
|
||||
|
||||
export function PostageStampSelector({ onSelect, defaultValue }: Props): ReactElement {
|
||||
const { stamps } = useContext(Context)
|
||||
const [selected, setSelected] = useState<string>(defaultValue ?? '')
|
||||
|
||||
function onChange(stampId: string) {
|
||||
setSelected(stampId)
|
||||
|
||||
if (!stamps) {
|
||||
return
|
||||
}
|
||||
|
||||
const stamp = stamps.find(x => x.batchID.toHex() === stampId)
|
||||
|
||||
if (stamp) {
|
||||
@@ -28,7 +33,7 @@ export function PostageStampSelector({ onSelect, defaultValue }: Props): ReactEl
|
||||
value: x.batchID.toHex(),
|
||||
}))}
|
||||
onChange={event => onChange(event.target.value as string)}
|
||||
defaultValue={defaultValue}
|
||||
value={selected}
|
||||
placeholder="Please select a postage stamp..."
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Box, Button, Grid, Slider, Typography } from '@material-ui/core'
|
||||
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'
|
||||
import { Duration, PostageBatchOptions, Size, Utils } from '@ethersphere/bee-js'
|
||||
import { Box, Button, Grid, Slider, Typography } from '@mui/material'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
import { makeStyles } from 'tss-react/mui'
|
||||
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { SwarmTextInput } from '../../components/SwarmTextInput'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
@@ -15,26 +16,24 @@ import { secondsToTimeString } from '../../utils'
|
||||
interface Props {
|
||||
onFinished: () => void
|
||||
}
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
link: {
|
||||
color: '#dd7700',
|
||||
textDecoration: 'underline',
|
||||
'&:hover': {
|
||||
textDecoration: 'none',
|
||||
const useStyles = makeStyles()(theme => ({
|
||||
link: {
|
||||
color: '#dd7700',
|
||||
textDecoration: 'underline',
|
||||
'&:hover': {
|
||||
textDecoration: 'none',
|
||||
|
||||
// https://github.com/mui-org/material-ui/issues/22543
|
||||
'@media (hover: none)': {
|
||||
textDecoration: 'none',
|
||||
},
|
||||
// https://github.com/mui-org/material-ui/issues/22543
|
||||
'@media (hover: none)': {
|
||||
textDecoration: 'none',
|
||||
},
|
||||
},
|
||||
buttonSelected: {
|
||||
color: 'white',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
buttonSelected: {
|
||||
color: 'white',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
},
|
||||
}))
|
||||
|
||||
const marks = [
|
||||
{ value: 1, label: '1 day' },
|
||||
@@ -42,7 +41,7 @@ const marks = [
|
||||
]
|
||||
|
||||
export function PostageStampStandardCreation({ onFinished }: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
const { classes } = useStyles()
|
||||
const { refresh } = useContext(StampsContext)
|
||||
const { beeApi } = useContext(SettingsContext)
|
||||
|
||||
@@ -100,7 +99,8 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
||||
await refresh()
|
||||
onFinished()
|
||||
} catch (e) {
|
||||
console.error(e) // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e)
|
||||
enqueueSnackbar(`Error: ${(e as Error).message}`, { variant: 'error' })
|
||||
}
|
||||
setSubmitting(false)
|
||||
@@ -139,7 +139,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
||||
</Box>
|
||||
<Box mb={2}>
|
||||
<Grid container justifyContent="space-between" spacing={2}>
|
||||
<Grid item xs={4}>
|
||||
<Grid sx={{ width: { xs: '100%', sm: '33.333%' } }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
fullWidth
|
||||
@@ -149,7 +149,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
||||
4 GB
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Grid sx={{ width: { xs: '100%', sm: '33.333%' } }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
fullWidth
|
||||
@@ -159,7 +159,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
||||
32 GB
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Grid sx={{ width: { xs: '100%', sm: '33.333%' } }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
fullWidth
|
||||
@@ -206,7 +206,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
||||
</Grid>
|
||||
</Box>
|
||||
<Grid container justifyContent="space-between" alignItems="center">
|
||||
<Grid item>
|
||||
<Grid>
|
||||
<SwarmButton
|
||||
disabled={submitting || !depthInput || !amountInput}
|
||||
onClick={submit}
|
||||
@@ -216,7 +216,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
||||
Buy New Stamp
|
||||
</SwarmButton>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Grid>
|
||||
<Link to={ROUTES.ACCOUNT_STAMPS_NEW_ADVANCED} className={classes.link}>
|
||||
Advanced mode
|
||||
</Link>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { createStyles, makeStyles, Theme } from '@material-ui/core'
|
||||
import Button from '@material-ui/core/Button'
|
||||
import Dialog from '@material-ui/core/Dialog'
|
||||
import DialogContent from '@material-ui/core/DialogContent'
|
||||
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||
import { Check, Clear } from '@material-ui/icons'
|
||||
import { Check, Clear } from '@mui/icons-material'
|
||||
import Button from '@mui/material/Button'
|
||||
import Dialog from '@mui/material/Dialog'
|
||||
import DialogContent from '@mui/material/DialogContent'
|
||||
import DialogTitle from '@mui/material/DialogTitle'
|
||||
import { ReactElement, useState } from 'react'
|
||||
import { makeStyles } from 'tss-react/mui'
|
||||
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { SwarmSelect } from '../../components/SwarmSelect'
|
||||
import { EnrichedPostageBatch } from '../../providers/Stamps'
|
||||
@@ -15,28 +16,26 @@ interface Props {
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
dialog: {
|
||||
background: theme.palette.background.default,
|
||||
borderRadius: 0,
|
||||
width: '100%',
|
||||
maxWidth: '890px',
|
||||
},
|
||||
title: {
|
||||
color: '#606060',
|
||||
textAlign: 'center',
|
||||
},
|
||||
hint: {
|
||||
marginBottom: '16px',
|
||||
},
|
||||
}),
|
||||
)
|
||||
const useStyles = makeStyles()(theme => ({
|
||||
dialog: {
|
||||
background: theme.palette.background.default,
|
||||
borderRadius: 0,
|
||||
width: '100%',
|
||||
maxWidth: '890px',
|
||||
},
|
||||
title: {
|
||||
color: '#606060',
|
||||
textAlign: 'center',
|
||||
},
|
||||
hint: {
|
||||
marginBottom: '16px',
|
||||
},
|
||||
}))
|
||||
|
||||
export function SelectPostageStampModal({ stamps, onSelect, onClose }: Props): ReactElement {
|
||||
const [selectedStamp, setSelectedStamp] = useState<EnrichedPostageBatch | null>(null)
|
||||
|
||||
const classes = useStyles()
|
||||
const { classes } = useStyles()
|
||||
|
||||
function onChange(stampId: string) {
|
||||
const stamp = stamps.find(x => x.batchID.toHex() === stampId)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ReactElement, useContext } from 'react'
|
||||
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'
|
||||
@@ -11,6 +12,7 @@ import { Context } from '../../providers/Settings'
|
||||
import { EnrichedPostageBatch } from '../../providers/Stamps'
|
||||
import { secondsToTimeString } from '../../utils'
|
||||
import { getHumanReadableFileSize } from '../../utils/file'
|
||||
|
||||
import { PostageStamp } from './PostageStamp'
|
||||
|
||||
interface Props {
|
||||
|
||||
+19
-19
@@ -1,33 +1,33 @@
|
||||
import { CircularProgress, Container } from '@material-ui/core'
|
||||
import { createStyles, makeStyles } from '@material-ui/core/styles'
|
||||
import { CircularProgress, Container } from '@mui/material'
|
||||
import { ReactElement, useContext, useEffect } from 'react'
|
||||
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
|
||||
import { useNavigate } from 'react-router'
|
||||
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
|
||||
import { makeStyles } from 'tss-react/mui'
|
||||
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as StampsContext } from '../../providers/Stamps'
|
||||
import { ROUTES } from '../../routes'
|
||||
|
||||
import StampsTable from './StampsTable'
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
root: {
|
||||
width: '100%',
|
||||
display: 'grid',
|
||||
},
|
||||
actions: {
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flex: '0 1 auto',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
},
|
||||
}),
|
||||
)
|
||||
const useStyles = makeStyles()(() => ({
|
||||
root: {
|
||||
width: '100%',
|
||||
display: 'grid',
|
||||
},
|
||||
actions: {
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flex: '0 1 auto',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
},
|
||||
}))
|
||||
|
||||
export default function Stamp(): ReactElement {
|
||||
const classes = useStyles()
|
||||
const { classes } = useStyles()
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ReactElement, ReactNode } from 'react'
|
||||
import { useContext } from 'react'
|
||||
|
||||
import ExpandableList from '../../../components/ExpandableList'
|
||||
import ExpandableListItemActions from '../../../components/ExpandableListItemActions'
|
||||
import ExpandableListItemKey from '../../../components/ExpandableListItemKey'
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { ReactElement, useContext } from 'react'
|
||||
|
||||
import CodeBlockTabs from '../../../components/CodeBlockTabs'
|
||||
import { Context as SettingsContext } from '../../../providers/Settings'
|
||||
import ExpandableList from '../../../components/ExpandableList'
|
||||
import ExpandableListItem from '../../../components/ExpandableListItem'
|
||||
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
|
||||
import ExpandableListItemInput from '../../../components/ExpandableListItemInput'
|
||||
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
|
||||
import StatusIcon from '../../../components/StatusIcon'
|
||||
import { CheckState, Context } from '../../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../../providers/Settings'
|
||||
|
||||
export default function NodeConnectionCheck(): ReactElement | null {
|
||||
const { setApiUrl, apiUrl, isDesktop } = useContext(SettingsContext)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ReactElement, ReactNode, useContext } from 'react'
|
||||
|
||||
import ExpandableList from '../../../components/ExpandableList'
|
||||
import ExpandableListItemNote from '../../../components/ExpandableListItemNote'
|
||||
import StatusIcon from '../../../components/StatusIcon'
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { ReactElement, useContext } from 'react'
|
||||
|
||||
import { Context } from '../../providers/Settings'
|
||||
|
||||
import ChequebookDeployFund from './SetupSteps/ChequebookDeployFund'
|
||||
import DesktopConnection from './SetupSteps/DesktopConnectionCheck'
|
||||
import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck'
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { DAI } from '@ethersphere/bee-js'
|
||||
import { Box, Grid, Typography } from '@material-ui/core'
|
||||
import { Box, Grid, Typography } from '@mui/material'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
|
||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
@@ -11,6 +12,7 @@ import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { SwarmDivider } from '../../components/SwarmDivider'
|
||||
import { Context } from '../../providers/Bee'
|
||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||
|
||||
import { TopUpProgressIndicator } from './TopUpProgressIndicator'
|
||||
|
||||
const MINIMUM_XDAI = DAI.fromDecimalString('0.5')
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Typography } from '@material-ui/core'
|
||||
import { Typography } from '@mui/material'
|
||||
import { ReactElement } from 'react'
|
||||
import Balance from './Balance'
|
||||
|
||||
import { ROUTES } from '../../routes'
|
||||
|
||||
import Balance from './Balance'
|
||||
|
||||
export function BankCardTopUpIndex(): ReactElement {
|
||||
return (
|
||||
<Balance
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Typography } from '@material-ui/core'
|
||||
import { Typography } from '@mui/material'
|
||||
import { ReactElement } from 'react'
|
||||
import Balance from './Balance'
|
||||
|
||||
import { ROUTES } from '../../routes'
|
||||
|
||||
import Balance from './Balance'
|
||||
|
||||
export function CryptoTopUpIndex(): ReactElement {
|
||||
return (
|
||||
<Balance
|
||||
@@ -1,10 +1,11 @@
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import { useNavigate, useParams } from 'react-router'
|
||||
import ArrowDown from 'remixicon-react/ArrowDownLineIcon'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
|
||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
@@ -54,7 +55,8 @@ export function GiftCardFund(): ReactElement {
|
||||
await restartBeeNode(desktopUrl)
|
||||
navigate(ROUTES.RESTART_LIGHT)
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error)
|
||||
enqueueSnackbar(`Failed to upgrade: ${error}`, { variant: 'error' })
|
||||
}
|
||||
}
|
||||
@@ -72,7 +74,8 @@ export function GiftCardFund(): ReactElement {
|
||||
|
||||
if (canUpgradeToLightNode) await restart()
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error)
|
||||
enqueueSnackbar(`Failed to fund: ${error}`, { variant: 'error' })
|
||||
} finally {
|
||||
setLoading(false)
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { BZZ, DAI } from '@ethersphere/bee-js'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { Wallet } from 'ethers'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
|
||||
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
import { ProgressIndicator } from '../../components/ProgressIndicator'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
@@ -12,7 +13,7 @@ import { SwarmDivider } from '../../components/SwarmDivider'
|
||||
import { SwarmTextInput } from '../../components/SwarmTextInput'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { Rpc } from '../../utils/rpc'
|
||||
import { RPC } from '../../utils/rpc'
|
||||
|
||||
export function GiftCardTopUpIndex(): ReactElement {
|
||||
const { rpcProvider } = useContext(SettingsContext)
|
||||
@@ -28,8 +29,8 @@ export function GiftCardTopUpIndex(): ReactElement {
|
||||
setLoading(true)
|
||||
try {
|
||||
const wallet = new Wallet(giftCode, rpcProvider)
|
||||
const dai = await Rpc._eth_getBalance(wallet.address, rpcProvider)
|
||||
const bzz = await Rpc._eth_getBalanceERC20(wallet.address, rpcProvider)
|
||||
const dai = await RPC._eth_getBalance(wallet.address, rpcProvider)
|
||||
const bzz = await RPC._eth_getBalanceERC20(wallet.address, rpcProvider)
|
||||
|
||||
if (dai.lt(DAI.fromDecimalString('0.001')) || bzz.lt(BZZ.fromDecimalString('0.001'))) {
|
||||
throw Error('Gift wallet does not have enough funds')
|
||||
@@ -37,7 +38,8 @@ export function GiftCardTopUpIndex(): ReactElement {
|
||||
enqueueSnackbar('Successfully verified gift wallet', { variant: 'success' })
|
||||
navigate(ROUTES.TOP_UP_GIFT_CODE_FUND.replace(':privateKeyString', giftCode))
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error)
|
||||
enqueueSnackbar(`Gift wallet could not be verified: ${error}`, { variant: 'error' })
|
||||
} finally {
|
||||
setLoading(false)
|
||||
@@ -1,10 +1,11 @@
|
||||
import { BeeModes, BZZ, DAI } from '@ethersphere/bee-js'
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { Box, Typography } from '@mui/material'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import ArrowDown from 'remixicon-react/ArrowDownCircleLineIcon'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
|
||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
@@ -18,7 +19,6 @@ import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { sleepMs } from '../../utils'
|
||||
import { isSwapError, SwapError, wrapWithSwapError } from '../../utils/SwapError'
|
||||
import {
|
||||
getBzzPriceAsDai,
|
||||
getDesktopConfiguration,
|
||||
@@ -26,7 +26,10 @@ import {
|
||||
restartBeeNode,
|
||||
upgradeToLightNode,
|
||||
} from '../../utils/desktop'
|
||||
import { Rpc } from '../../utils/rpc'
|
||||
import { isSwapError, SwapError, wrapWithSwapError } from '../../utils/errors'
|
||||
import { LocalStorageKeys } from '../../utils/localStorage'
|
||||
import { RPC } from '../../utils/rpc'
|
||||
|
||||
import { TopUpProgressIndicator } from './TopUpProgressIndicator'
|
||||
|
||||
const MINIMUM_XDAI = DAI.fromDecimalString('0.1')
|
||||
@@ -126,7 +129,8 @@ export function Swap({ header }: Props): ReactElement {
|
||||
|
||||
navigate(ROUTES.RESTART_LIGHT)
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error)
|
||||
enqueueSnackbar(`Failed to upgrade: ${error}`, { variant: 'error' })
|
||||
}
|
||||
}
|
||||
@@ -142,7 +146,7 @@ export function Swap({ header }: Props): ReactElement {
|
||||
}
|
||||
|
||||
async function performSwapWithChecks(daiToSwap: DAI) {
|
||||
if (!localStorage.getItem('apiKey')) {
|
||||
if (!localStorage.getItem(LocalStorageKeys.apiKey)) {
|
||||
throw new SwapError('API key is not set, reopen dashboard through Swarm Desktop')
|
||||
}
|
||||
|
||||
@@ -162,7 +166,7 @@ export function Swap({ header }: Props): ReactElement {
|
||||
throw new SwapError('Blockchain RPC endpoint is not configured in Swarm Desktop')
|
||||
}
|
||||
await wrapWithSwapError(
|
||||
Rpc.getNetworkChainId(desktopConfiguration['blockchain-rpc-endpoint']),
|
||||
RPC.getNetworkChainId(desktopConfiguration['blockchain-rpc-endpoint']),
|
||||
`Blockchain RPC endpoint not reachable at ${desktopConfiguration['blockchain-rpc-endpoint']}`,
|
||||
)
|
||||
await wrapWithSwapError(sendSwapRequest(daiToSwap), GENERIC_SWAP_FAILED_ERROR_MESSAGE)
|
||||
@@ -191,12 +195,14 @@ export function Swap({ header }: Props): ReactElement {
|
||||
enqueueSnackbar(error.snackbarMessage, { variant: 'error' })
|
||||
|
||||
if (error.originalError) {
|
||||
console.error(error.originalError) // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error.originalError)
|
||||
}
|
||||
} else {
|
||||
// we have an unexpected error
|
||||
enqueueSnackbar(`${GENERIC_SWAP_FAILED_ERROR_MESSAGE} ${error}`, { variant: 'error' })
|
||||
console.error(error) // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error)
|
||||
}
|
||||
} finally {
|
||||
setLoading(false)
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
import { ProgressIndicator } from '../../components/ProgressIndicator'
|
||||
|
||||
interface Props {
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BeeModes, BZZ, DAI } from '@ethersphere/bee-js'
|
||||
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
|
||||
import { Box, Grid, Typography } from '@mui/material'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
@@ -8,37 +8,37 @@ import Check from 'remixicon-react/CheckLineIcon'
|
||||
import Download from 'remixicon-react/DownloadLineIcon'
|
||||
import Gift from 'remixicon-react/GiftLineIcon'
|
||||
import MoneyDollarCircle from 'remixicon-react/MoneyDollarCircleLineIcon'
|
||||
import { makeStyles } from 'tss-react/mui'
|
||||
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
import { Loading } from '../../components/Loading'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext, CheckState } from '../../providers/Bee'
|
||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
checkWrapper: {
|
||||
background: 'rgba(0, 230, 118, 0.25)',
|
||||
borderRadius: 99999,
|
||||
width: '180px',
|
||||
height: '180px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
}),
|
||||
)
|
||||
const useStyles = makeStyles()(() => ({
|
||||
checkWrapper: {
|
||||
background: 'rgba(0, 230, 118, 0.25)',
|
||||
borderRadius: 99999,
|
||||
width: '180px',
|
||||
height: '180px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
}))
|
||||
|
||||
const MINIMUM_XDAI = DAI.fromDecimalString('0.05')
|
||||
const MINIMUM_XBZZ = BZZ.fromDecimalString('0.1')
|
||||
|
||||
export default function TopUp(): ReactElement {
|
||||
const navigate = useNavigate()
|
||||
const styles = useStyles()
|
||||
const { classes } = useStyles()
|
||||
const { isDesktop, desktopUrl } = useContext(SettingsContext)
|
||||
const { nodeInfo, status } = useContext(BeeContext)
|
||||
const { balance } = useContext(BalanceProvider)
|
||||
@@ -59,7 +59,8 @@ export default function TopUp(): ReactElement {
|
||||
await restartBeeNode(desktopUrl)
|
||||
navigate(ROUTES.RESTART_LIGHT)
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error)
|
||||
enqueueSnackbar(`Failed to upgrade: ${error}`, { variant: 'error' })
|
||||
}
|
||||
setLoading(false)
|
||||
@@ -76,7 +77,7 @@ export default function TopUp(): ReactElement {
|
||||
<HistoryHeader>Account</HistoryHeader>
|
||||
<Grid container direction="column" alignItems="center">
|
||||
<Box mb={6}>
|
||||
<div className={styles.checkWrapper}>
|
||||
<div className={classes.checkWrapper}>
|
||||
<Download size={100} color="#ededed" />
|
||||
</div>
|
||||
</Box>
|
||||
Reference in New Issue
Block a user