feat: add identity and feed management (#272)
* feat(wip): add basic feed operations * ci: bump checks * ci: bump checks * feat: rework stamps and add feed functionalities * refactor: polish and fixes * feat(wip): add formulas * feat: show bzz.link for websites * feat: add stamp empty states and formatBzz * feat: add feed download * chore: update manifest-js version * feat: dev mode support with bee-js 3.1.0 (#273) * feat: dev mode support with bee-js 3.1.0 * fix: added missing package-lock.json file * build: remove PR preview * style: work on design * feat: add TroubleshootConnectionCard * build: remove depcheck Co-authored-by: Attila Gazso <agazso@gmail.com>
This commit is contained in:
@@ -1,24 +1,35 @@
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import * as swarmCid from '@ethersphere/swarm-cid'
|
||||
import { Box } from '@material-ui/core'
|
||||
import { ReactElement } from 'react'
|
||||
import { DocumentationText } from '../../components/DocumentationText'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
import ExpandableListItemLink from '../../components/ExpandableListItemLink'
|
||||
import { detectIndexHtml } from '../../utils/file'
|
||||
import { SwarmFile } from '../../utils/SwarmFile'
|
||||
|
||||
interface Props {
|
||||
files: SwarmFile[]
|
||||
hash: string
|
||||
}
|
||||
|
||||
export function AssetSummary({ hash }: Props): ReactElement {
|
||||
export function AssetSummary({ files, hash }: Props): ReactElement {
|
||||
return (
|
||||
<>
|
||||
<Box mb={4}>
|
||||
<ExpandableListItemKey label="Swarm hash" value={hash} />
|
||||
<ExpandableListItemLink label="Share on Swarm Gateway" value={`https://gateway.ethswarm.org/access/${hash}`} />
|
||||
{detectIndexHtml(files) && (
|
||||
<ExpandableListItemLink
|
||||
label="BZZ Link"
|
||||
value={`https://${swarmCid.encodeManifestReference(hash).toString()}.bzz.link`}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<Typography>
|
||||
<DocumentationText>
|
||||
The Swarm Gateway is graciously provided by the Swarm Foundation. This service is under development and provided
|
||||
for testing purposes only. Learn more at{' '}
|
||||
<a href="https://gateway.ethswarm.org/">https://gateway.ethswarm.org/</a>.
|
||||
</Typography>
|
||||
</DocumentationText>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ReactElement, useContext, useState } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import ExpandableListItemInput from '../../components/ExpandableListItemInput'
|
||||
import { History } from '../../components/History'
|
||||
import { Context, defaultUploadOrigin } from '../../providers/File'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { extractSwarmHash } from '../../utils'
|
||||
@@ -16,6 +17,8 @@ export function Download(): ReactElement {
|
||||
const { beeApi } = useContext(SettingsContext)
|
||||
const [referenceError, setReferenceError] = useState<string | undefined>(undefined)
|
||||
|
||||
const { setUploadOrigin } = useContext(Context)
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
const history = useHistory()
|
||||
|
||||
@@ -28,12 +31,21 @@ export function Download(): ReactElement {
|
||||
}
|
||||
|
||||
async function onSwarmIdentifier(identifier: string) {
|
||||
setLoading(true)
|
||||
|
||||
if (!beeApi) {
|
||||
setLoading(false)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const manifestJs = new ManifestJs(beeApi)
|
||||
const feedIdentifier = await manifestJs.resolveFeedManifest(identifier)
|
||||
|
||||
if (feedIdentifier) {
|
||||
identifier = feedIdentifier
|
||||
}
|
||||
const isManifest = await manifestJs.isManifest(identifier)
|
||||
|
||||
if (!isManifest) {
|
||||
@@ -41,6 +53,7 @@ export function Download(): ReactElement {
|
||||
}
|
||||
const indexDocument = await manifestJs.getIndexDocumentPath(identifier)
|
||||
putHistory(HISTORY_KEYS.DOWNLOAD_HISTORY, identifier, determineHistoryName(identifier, indexDocument))
|
||||
setUploadOrigin(defaultUploadOrigin)
|
||||
history.push(ROUTES.HASH.replace(':hash', identifier))
|
||||
} catch (error: unknown) {
|
||||
let message = typeof error === 'object' && error !== null && Reflect.get(error, 'message')
|
||||
@@ -80,11 +93,12 @@ export function Download(): ReactElement {
|
||||
onConfirm={value => onSwarmIdentifier(value)}
|
||||
onChange={validateChange}
|
||||
helperText={referenceError}
|
||||
confirmLabel={'Search'}
|
||||
confirmLabel={'Find'}
|
||||
confirmLabelDisabled={Boolean(referenceError) || loading}
|
||||
placeholder="e.g. 31fb0362b1a42536134c86bc58b97ac0244e5c6630beec3e27c2d1cecb38c605"
|
||||
expandedOnly
|
||||
mapperFn={value => recognizeSwarmHash(value)}
|
||||
loading={loading}
|
||||
/>
|
||||
<History title="Download History" localStorageKey={HISTORY_KEYS.DOWNLOAD_HISTORY} />
|
||||
</>
|
||||
|
||||
@@ -1,32 +1,46 @@
|
||||
import { Button } from '@material-ui/core'
|
||||
import { Clear } from '@material-ui/icons'
|
||||
import { Box, Grid } from '@material-ui/core'
|
||||
import { ReactElement } from 'react'
|
||||
import { Download, Link } from 'react-feather'
|
||||
import { Bookmark, Download, Link, X } from 'react-feather'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
|
||||
interface Props {
|
||||
onOpen: () => void
|
||||
onDownload: () => void
|
||||
onCancel: () => void
|
||||
onDownload: () => void
|
||||
onUpdateFeed: () => void
|
||||
hasIndexDocument: boolean
|
||||
loading: boolean
|
||||
}
|
||||
|
||||
export function DownloadActionBar({ onOpen, onDownload, onCancel, hasIndexDocument, loading }: Props): ReactElement {
|
||||
export function DownloadActionBar({
|
||||
onOpen,
|
||||
onCancel,
|
||||
onDownload,
|
||||
onUpdateFeed,
|
||||
hasIndexDocument,
|
||||
loading,
|
||||
}: Props): ReactElement {
|
||||
return (
|
||||
<ExpandableListItemActions>
|
||||
{hasIndexDocument && (
|
||||
<SwarmButton onClick={onOpen} iconType={Link} disabled={loading}>
|
||||
View Website
|
||||
<Grid container justifyContent="space-between">
|
||||
<ExpandableListItemActions>
|
||||
{hasIndexDocument && (
|
||||
<SwarmButton onClick={onOpen} iconType={Link} disabled={loading}>
|
||||
View Website
|
||||
</SwarmButton>
|
||||
)}
|
||||
<SwarmButton onClick={onDownload} iconType={Download} disabled={loading} loading={loading}>
|
||||
Download
|
||||
</SwarmButton>
|
||||
)}
|
||||
<SwarmButton onClick={onDownload} iconType={Download} disabled={loading} loading={loading}>
|
||||
Download
|
||||
</SwarmButton>
|
||||
<Button onClick={onCancel} variant="contained" startIcon={<Clear />} disabled={loading}>
|
||||
Close
|
||||
</Button>
|
||||
</ExpandableListItemActions>
|
||||
<SwarmButton onClick={onCancel} iconType={X} disabled={loading} loading={loading} cancel>
|
||||
Close
|
||||
</SwarmButton>
|
||||
</ExpandableListItemActions>
|
||||
<Box mb={1} mr={1}>
|
||||
<SwarmButton onClick={onUpdateFeed} iconType={Bookmark}>
|
||||
Update Feed
|
||||
</SwarmButton>
|
||||
</Box>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { ManifestJs } from '@ethersphere/manifest-js'
|
||||
import { Box } from '@material-ui/core'
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { saveAs } from 'file-saver'
|
||||
import JSZip from 'jszip'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import { RouteComponentProps, useHistory } from 'react-router-dom'
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
import { Loading } from '../../components/Loading'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { convertBeeFileToBrowserFile, convertManifestToFiles } from '../../utils/file'
|
||||
@@ -21,17 +25,22 @@ interface MatchParams {
|
||||
|
||||
export function Share(props: RouteComponentProps<MatchParams>): ReactElement {
|
||||
const { apiUrl, beeApi } = useContext(SettingsContext)
|
||||
const { status } = useContext(BeeContext)
|
||||
|
||||
const reference = props.match.params.hash
|
||||
|
||||
const history = useHistory()
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [downloading, setDownloading] = useState(false)
|
||||
const [files, setFiles] = useState<SwarmFile[]>([])
|
||||
const [swarmEntries, setSwarmEntries] = useState<Record<string, string>>({})
|
||||
const [indexDocument, setIndexDocument] = useState<string | null>(null)
|
||||
const [notFound, setNotFound] = useState(false)
|
||||
|
||||
async function prepare() {
|
||||
if (!beeApi) {
|
||||
if (!beeApi || !status.all) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -39,7 +48,10 @@ export function Share(props: RouteComponentProps<MatchParams>): ReactElement {
|
||||
const isManifest = await manifestJs.isManifest(reference)
|
||||
|
||||
if (!isManifest) {
|
||||
throw Error('The specified hash does not contain valid content.')
|
||||
setNotFound(true)
|
||||
enqueueSnackbar('The specified hash does not contain valid content.', { variant: 'error' })
|
||||
|
||||
return
|
||||
}
|
||||
const entries = await manifestJs.getHashes(reference)
|
||||
setSwarmEntries(entries)
|
||||
@@ -67,9 +79,13 @@ export function Share(props: RouteComponentProps<MatchParams>): ReactElement {
|
||||
}
|
||||
}
|
||||
|
||||
function onUpdateFeed() {
|
||||
history.push(ROUTES.FEEDS_UPDATE.replace(':hash', reference))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
prepare().then(() => {
|
||||
prepare().finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -97,22 +113,34 @@ export function Share(props: RouteComponentProps<MatchParams>): ReactElement {
|
||||
|
||||
const assetName = shortenHash(reference)
|
||||
|
||||
if (!status.all) return <TroubleshootConnectionCard />
|
||||
|
||||
if (loading) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
if (notFound) {
|
||||
return (
|
||||
<>
|
||||
<HistoryHeader>Not Found</HistoryHeader>
|
||||
<Typography>The specified hash is not found.</Typography>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box mb={4}>
|
||||
<AssetPreview files={files} assetName={assetName} />
|
||||
</Box>
|
||||
<Box mb={4}>
|
||||
<AssetSummary hash={reference} />
|
||||
<AssetSummary files={files} hash={reference} />
|
||||
</Box>
|
||||
<DownloadActionBar
|
||||
onOpen={onOpen}
|
||||
onCancel={onClose}
|
||||
onDownload={onDownload}
|
||||
onUpdateFeed={onUpdateFeed}
|
||||
hasIndexDocument={Boolean(indexDocument && files.length > 1)}
|
||||
loading={downloading}
|
||||
/>
|
||||
|
||||
+107
-35
@@ -1,41 +1,71 @@
|
||||
import { Box } from '@material-ui/core'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import { useHistory } 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 { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as IdentityContext, Identity } from '../../providers/Feeds'
|
||||
import { Context as FileContext } from '../../providers/File'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context, EnrichedPostageBatch } from '../../providers/Stamps'
|
||||
import { Context as StampsContext, EnrichedPostageBatch } from '../../providers/Stamps'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { detectIndexHtml, getAssetNameFromFiles } from '../../utils/file'
|
||||
import { persistIdentity, updateFeed } from '../../utils/identity'
|
||||
import { HISTORY_KEYS, putHistory } from '../../utils/local-storage'
|
||||
import { CreatePostageStampModal } from '../stamps/CreatePostageStampModal'
|
||||
import { SelectPostageStampModal } from '../stamps/SelectPostageStampModal'
|
||||
import { FeedPasswordDialog } from '../feeds/FeedPasswordDialog'
|
||||
import { PostageStampCreation } from '../stamps/PostageStampCreation'
|
||||
import { PostageStampSelector } from '../stamps/PostageStampSelector'
|
||||
import { AssetPreview } from './AssetPreview'
|
||||
import { StampPreview } from './StampPreview'
|
||||
import { UploadActionBar } from './UploadActionBar'
|
||||
|
||||
export function Upload(): ReactElement {
|
||||
const [isBuyingStamp, setBuyingStamp] = useState(false)
|
||||
const [isSelectingStamp, setSelectingStamp] = useState(false)
|
||||
const [step, setStep] = useState(0)
|
||||
const [stampMode, setStampMode] = useState<'SELECT' | 'BUY'>('SELECT')
|
||||
const [stamp, setStamp] = useState<EnrichedPostageBatch | null>(null)
|
||||
const [isUploading, setUploading] = useState(false)
|
||||
const [showPasswordPrompt, setShowPasswordPrompt] = useState(false)
|
||||
|
||||
const { stamps, refresh } = useContext(Context)
|
||||
const { refresh } = useContext(StampsContext)
|
||||
const { beeApi } = useContext(SettingsContext)
|
||||
const { files, setFiles } = useContext(FileContext)
|
||||
const { files, setFiles, uploadOrigin } = useContext(FileContext)
|
||||
const { identities, setIdentities } = useContext(IdentityContext)
|
||||
const { status } = useContext(BeeContext)
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
const history = useHistory()
|
||||
|
||||
if (!files.length) {
|
||||
setFiles([])
|
||||
history.replace(ROUTES.UPLOAD)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
refresh()
|
||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const uploadFiles = () => {
|
||||
if (!status.all) return <TroubleshootConnectionCard />
|
||||
|
||||
if (!files.length) {
|
||||
setFiles([])
|
||||
history.replace(ROUTES.UPLOAD)
|
||||
|
||||
return <></>
|
||||
}
|
||||
|
||||
const identity = uploadOrigin.uuid ? identities.find(x => x.uuid === uploadOrigin.uuid) : null
|
||||
|
||||
const onUpload = () => {
|
||||
if (uploadOrigin.origin === 'UPLOAD') {
|
||||
uploadFiles()
|
||||
} else {
|
||||
if ((identity as Identity).type === 'PRIVATE_KEY') {
|
||||
uploadFiles()
|
||||
} else {
|
||||
setShowPasswordPrompt(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uploadFiles = (password?: string) => {
|
||||
if (!beeApi || !files.length || !stamp) {
|
||||
return
|
||||
}
|
||||
@@ -48,7 +78,16 @@ export function Upload(): ReactElement {
|
||||
.uploadFiles(stamp.batchID, files as unknown as File[], { indexDocument })
|
||||
.then(hash => {
|
||||
putHistory(HISTORY_KEYS.UPLOAD_HISTORY, hash.reference, getAssetNameFromFiles(files))
|
||||
history.replace(ROUTES.HASH.replace(':hash', hash.reference))
|
||||
|
||||
if (uploadOrigin.origin === 'UPLOAD') {
|
||||
history.replace(ROUTES.HASH.replace(':hash', hash.reference))
|
||||
} else {
|
||||
updateFeed(beeApi, identity as Identity, hash.reference, stamp.batchID, password as string).then(() => {
|
||||
persistIdentity(identities, identity as Identity)
|
||||
setIdentities([...identities])
|
||||
history.replace(ROUTES.FEEDS_PAGE.replace(':uuid', uploadOrigin.uuid as string))
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
enqueueSnackbar(`Error uploading: ${e.message}`, { variant: 'error' })
|
||||
@@ -57,36 +96,69 @@ export function Upload(): ReactElement {
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
setStep(0)
|
||||
setFiles([])
|
||||
setStamp(null)
|
||||
setUploading(false)
|
||||
}
|
||||
|
||||
const onFeedPasswordGiven = (password: string) => {
|
||||
uploadFiles(password)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HistoryHeader>Upload</HistoryHeader>
|
||||
{files.length && <AssetPreview files={files} />}
|
||||
{stamp !== null ? <StampPreview stamp={stamp} /> : null}
|
||||
{files.length && (
|
||||
<UploadActionBar
|
||||
canSelectStamp={stamps !== null && stamps.length > 0}
|
||||
hasSelectedStamp={stamp !== null}
|
||||
onCancel={reset}
|
||||
onBuy={() => setBuyingStamp(true)}
|
||||
onSelect={() => setSelectingStamp(true)}
|
||||
onUpload={uploadFiles}
|
||||
onClearStamp={() => setStamp(null)}
|
||||
isUploading={isUploading}
|
||||
{showPasswordPrompt && (
|
||||
<FeedPasswordDialog
|
||||
loading={isUploading}
|
||||
feedName={(identity as Identity).name}
|
||||
onCancel={() => setShowPasswordPrompt(false)}
|
||||
onProceed={onFeedPasswordGiven}
|
||||
/>
|
||||
)}
|
||||
{isBuyingStamp ? <CreatePostageStampModal onClose={() => setBuyingStamp(false)} /> : null}
|
||||
{stamps && isSelectingStamp ? (
|
||||
<SelectPostageStampModal
|
||||
stamps={stamps}
|
||||
onClose={() => setSelectingStamp(false)}
|
||||
onSelect={stamp => setStamp(stamp)}
|
||||
/>
|
||||
) : null}
|
||||
{identity && <HistoryHeader>{`Update "${identity.name}"`}</HistoryHeader>}
|
||||
{!identity && <HistoryHeader>Upload</HistoryHeader>}
|
||||
<Box mb={4}>
|
||||
<ProgressIndicator steps={['Preview', 'Add postage stamp', 'Upload to node']} index={step} />
|
||||
</Box>
|
||||
{(step === 0 || step === 2) && <AssetPreview files={files} />}
|
||||
{step === 1 && (
|
||||
<>
|
||||
<Box mb={2}>
|
||||
{stampMode === 'SELECT' ? (
|
||||
<PostageStampSelector onSelect={stamp => setStamp(stamp)} defaultValue={stamp?.batchID} />
|
||||
) : (
|
||||
<PostageStampCreation onFinished={() => setStampMode('SELECT')} />
|
||||
)}
|
||||
</Box>
|
||||
<Box mb={4}>
|
||||
<DocumentationText>
|
||||
Please refer to the{' '}
|
||||
<a
|
||||
href="https://docs.ethswarm.org/debug-api/#tag/Postage-Stamps/paths/~1stamps~1{amount}~1{depth}/post"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
official Bee documentation
|
||||
</a>{' '}
|
||||
to understand these values.
|
||||
</DocumentationText>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
{step === 2 && stamp && <StampPreview stamp={stamp} />}
|
||||
<UploadActionBar
|
||||
step={step}
|
||||
onCancel={reset}
|
||||
onGoBack={() => setStep(step => step - 1)}
|
||||
onProceed={() => setStep(step => step + 1)}
|
||||
onUpload={onUpload}
|
||||
isUploading={isUploading}
|
||||
hasStamp={Boolean(stamp)}
|
||||
uploadLabel={identity ? 'Update Feed' : 'Upload To Your Node'}
|
||||
stampMode={stampMode}
|
||||
setStampMode={setStampMode}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,69 +1,88 @@
|
||||
import { Button, Typography } from '@material-ui/core'
|
||||
import { Clear } from '@material-ui/icons'
|
||||
import { Box, Grid } from '@material-ui/core'
|
||||
import { ReactElement } from 'react'
|
||||
import { Check, Layers, PlusSquare, RefreshCcw } from 'react-feather'
|
||||
import { ArrowLeft, Check, Layers, PlusSquare, X } from 'react-feather'
|
||||
import { DocumentationText } from '../../components/DocumentationText'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
|
||||
interface Props {
|
||||
canSelectStamp: boolean
|
||||
hasSelectedStamp: boolean
|
||||
step: number
|
||||
onUpload: () => void
|
||||
onBuy: () => void
|
||||
onSelect: () => void
|
||||
onCancel: () => void
|
||||
onClearStamp: () => void
|
||||
onGoBack: () => void
|
||||
onProceed: () => void
|
||||
isUploading: boolean
|
||||
hasStamp: boolean
|
||||
uploadLabel: string
|
||||
stampMode: 'BUY' | 'SELECT'
|
||||
setStampMode: (mode: 'BUY' | 'SELECT') => void
|
||||
}
|
||||
|
||||
export function UploadActionBar({
|
||||
canSelectStamp,
|
||||
hasSelectedStamp,
|
||||
step,
|
||||
onUpload,
|
||||
onBuy,
|
||||
onSelect,
|
||||
onCancel,
|
||||
onClearStamp,
|
||||
onGoBack,
|
||||
onProceed,
|
||||
isUploading,
|
||||
hasStamp,
|
||||
uploadLabel,
|
||||
stampMode,
|
||||
setStampMode,
|
||||
}: Props): ReactElement {
|
||||
const showBuy = !hasSelectedStamp
|
||||
const showSelect = canSelectStamp && !hasSelectedStamp
|
||||
const showUpload = hasSelectedStamp
|
||||
const showChange = canSelectStamp && hasSelectedStamp
|
||||
if (step === 0) {
|
||||
return (
|
||||
<>
|
||||
<Box mb={1}>
|
||||
<ExpandableListItemActions>
|
||||
<SwarmButton onClick={onProceed} iconType={Layers}>
|
||||
Add Postage Stamp
|
||||
</SwarmButton>
|
||||
<SwarmButton onClick={onCancel} iconType={X} cancel>
|
||||
Cancel
|
||||
</SwarmButton>
|
||||
</ExpandableListItemActions>
|
||||
</Box>
|
||||
<DocumentationText>You need a postage stamp to upload.</DocumentationText>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
if (step === 1) {
|
||||
return (
|
||||
<Grid container direction="row" justifyContent="space-between">
|
||||
<ExpandableListItemActions>
|
||||
{stampMode === 'SELECT' && (
|
||||
<SwarmButton onClick={onProceed} iconType={Check} disabled={!hasStamp}>
|
||||
Proceed With Selected Stamp
|
||||
</SwarmButton>
|
||||
)}
|
||||
<SwarmButton onClick={onGoBack} iconType={ArrowLeft} cancel>
|
||||
Back To Preview
|
||||
</SwarmButton>
|
||||
</ExpandableListItemActions>
|
||||
<SwarmButton
|
||||
onClick={() => setStampMode(stampMode === 'BUY' ? 'SELECT' : 'BUY')}
|
||||
iconType={stampMode === 'BUY' ? Layers : PlusSquare}
|
||||
>
|
||||
{stampMode === 'BUY' ? 'Use Existing Stamp' : 'Buy New Stamp'}
|
||||
</SwarmButton>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
if (step === 2) {
|
||||
return (
|
||||
<ExpandableListItemActions>
|
||||
{showBuy ? (
|
||||
<SwarmButton onClick={onBuy} iconType={PlusSquare}>
|
||||
Buy New Postage Stamp
|
||||
</SwarmButton>
|
||||
) : null}
|
||||
{showSelect ? (
|
||||
<SwarmButton onClick={onSelect} iconType={Layers}>
|
||||
Use Existing Postage Stamp
|
||||
</SwarmButton>
|
||||
) : null}
|
||||
{showUpload ? (
|
||||
<SwarmButton onClick={onUpload} iconType={Check} disabled={isUploading} loading={isUploading}>
|
||||
Upload To Your Node
|
||||
</SwarmButton>
|
||||
) : null}
|
||||
{showChange ? (
|
||||
<SwarmButton onClick={onClearStamp} iconType={RefreshCcw} disabled={isUploading}>
|
||||
Change Postage Stamp
|
||||
</SwarmButton>
|
||||
) : null}
|
||||
<Button onClick={onCancel} variant="contained" startIcon={<Clear />}>
|
||||
Cancel
|
||||
</Button>
|
||||
<SwarmButton onClick={onUpload} iconType={Check} disabled={isUploading} loading={isUploading}>
|
||||
{uploadLabel}
|
||||
</SwarmButton>
|
||||
<SwarmButton onClick={onGoBack} iconType={ArrowLeft} disabled={isUploading} cancel>
|
||||
Change Postage Stamp
|
||||
</SwarmButton>
|
||||
</ExpandableListItemActions>
|
||||
{showSelect ? (
|
||||
<Typography>
|
||||
You need a postage stamp to upload. Please refer to the official Bee documentation to understand how postage
|
||||
stamps work.
|
||||
</Typography>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return <></>
|
||||
}
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import { createStyles, makeStyles, Theme, Typography } from '@material-ui/core'
|
||||
import { createStyles, makeStyles, Theme } from '@material-ui/core'
|
||||
import { DropzoneArea } from 'material-ui-dropzone'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { FilePlus, FolderPlus, PlusCircle } from 'react-feather'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { DocumentationText } from '../../components/DocumentationText'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { Context } from '../../providers/File'
|
||||
import { Context, UploadOrigin } from '../../providers/File'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { detectIndexHtml } from '../../utils/file'
|
||||
import { SwarmFile } from '../../utils/SwarmFile'
|
||||
|
||||
interface Props {
|
||||
maximumSizeInBytes: number
|
||||
uploadOrigin: UploadOrigin
|
||||
showHelp: boolean
|
||||
}
|
||||
|
||||
const MAX_FILE_SIZE = 1_000_000_000 // 1 gigabyte
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
areaWrapper: { position: 'relative', marginBottom: theme.spacing(2) },
|
||||
@@ -44,8 +48,8 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
}),
|
||||
)
|
||||
|
||||
export function UploadArea({ maximumSizeInBytes }: Props): ReactElement {
|
||||
const { setFiles } = useContext(Context)
|
||||
export function UploadArea({ uploadOrigin, showHelp }: Props): ReactElement {
|
||||
const { setFiles, setUploadOrigin } = useContext(Context)
|
||||
const classes = useStyles()
|
||||
const history = useHistory()
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
@@ -110,6 +114,7 @@ export function UploadArea({ maximumSizeInBytes }: Props): ReactElement {
|
||||
setFiles(swarmFiles)
|
||||
|
||||
if (files.length) {
|
||||
setUploadOrigin(uploadOrigin)
|
||||
history.push(ROUTES.UPLOAD_IN_PROGRESS)
|
||||
}
|
||||
}
|
||||
@@ -123,7 +128,7 @@ export function UploadArea({ maximumSizeInBytes }: Props): ReactElement {
|
||||
dropzoneClass={classes.dropzone}
|
||||
onChange={handleChange}
|
||||
filesLimit={1e9}
|
||||
maxFileSize={maximumSizeInBytes}
|
||||
maxFileSize={MAX_FILE_SIZE}
|
||||
showPreviews={false}
|
||||
/>
|
||||
<div className={classes.buttonWrapper}>
|
||||
@@ -138,10 +143,12 @@ export function UploadArea({ maximumSizeInBytes }: Props): ReactElement {
|
||||
</SwarmButton>
|
||||
</div>
|
||||
</div>
|
||||
<Typography>
|
||||
You can click the buttons above or simply drag and drop to add a file or folder. To upload a website to Swarm,
|
||||
make sure that your folder contains an “index.html” file.
|
||||
</Typography>
|
||||
{showHelp && (
|
||||
<DocumentationText>
|
||||
You can click the buttons above or simply drag and drop to add a file or folder. To upload a website to Swarm,
|
||||
make sure that your folder contains an “index.html” file.
|
||||
</DocumentationText>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import { ReactElement } from 'react'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import { History } from '../../components/History'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { defaultUploadOrigin } from '../../providers/File'
|
||||
import { HISTORY_KEYS } from '../../utils/local-storage'
|
||||
import { FileNavigation } from './FileNavigation'
|
||||
import { UploadArea } from './UploadArea'
|
||||
|
||||
const MAX_FILE_SIZE = 1_000_000_000 // 1 gigabyte
|
||||
|
||||
export function UploadLander(): ReactElement {
|
||||
const { status } = useContext(BeeContext)
|
||||
|
||||
if (!status.all) return <TroubleshootConnectionCard />
|
||||
|
||||
return (
|
||||
<>
|
||||
<FileNavigation active="UPLOAD" />
|
||||
<UploadArea maximumSizeInBytes={MAX_FILE_SIZE} />
|
||||
<UploadArea showHelp={true} uploadOrigin={defaultUploadOrigin} />
|
||||
<History title="Upload History" localStorageKey={HISTORY_KEYS.UPLOAD_HISTORY} />
|
||||
</>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user