feat: modularisation (#244)
* chore: gitignore for lib directory * build: packageing for webpack lib build * build: webpack config * feat: expose App component with beeApiUrl parameter * build: tsconfig for library build * build: main property of package json for tsc build * refactor: rename beeUrl option to beeApiUrl * refactor: manange config class instead of process.env calls * build: babelrc config * build: babel plugins and presets for webpack build * chore: serve.js chmod * build(refactor): webpack build * refactor: number notation * chore: webpack and package config change * build: add babel preset-env * chore: prepare script also builds component lib * feat: typegen * revert: set back prepare command * build: assets loader config * feat: beeDebugApiUrl * refactor: move test files to the test folder because of typegen * feat: locked api settings * chore: depcheck ignores * chore: types check script * ci: check types * ci: publish with library * chore: add webpack as devDep * chore: locked semver * chore: remove debug logging * style: webpack config * chore: react and react-dom as dependency * chore: package-lock * fix: clean package-lock init * refactor: fix versions in package.json
This commit is contained in:
+9
-3
@@ -1,7 +1,7 @@
|
||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||
import { ThemeProvider } from '@material-ui/core/styles'
|
||||
import { SnackbarProvider } from 'notistack'
|
||||
import { ReactElement } from 'react'
|
||||
import React, { ReactElement } from 'react'
|
||||
import { BrowserRouter as Router } from 'react-router-dom'
|
||||
import './App.css'
|
||||
import Dashboard from './layout/Dashboard'
|
||||
@@ -13,10 +13,16 @@ import { Provider as StampsProvider } from './providers/Stamps'
|
||||
import BaseRouter from './routes'
|
||||
import { theme } from './theme'
|
||||
|
||||
const App = (): ReactElement => (
|
||||
interface Props {
|
||||
beeApiUrl?: string
|
||||
beeDebugApiUrl?: string
|
||||
lockedApiSettings?: boolean
|
||||
}
|
||||
|
||||
const App = ({ beeApiUrl, beeDebugApiUrl, lockedApiSettings }: Props): ReactElement => (
|
||||
<div className="App">
|
||||
<ThemeProvider theme={theme}>
|
||||
<SettingsProvider>
|
||||
<SettingsProvider beeApiUrl={beeApiUrl} beeDebugApiUrl={beeDebugApiUrl} lockedApiSettings={lockedApiSettings}>
|
||||
<BeeProvider>
|
||||
<StampsProvider>
|
||||
<FileProvider>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
|
||||
import { Alert, AlertTitle } from '@material-ui/lab'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
const LIMIT = 100_000_000 // 100 megabytes
|
||||
const LIMIT = 100000000 // 100 megabytes
|
||||
|
||||
interface Props {
|
||||
files: File[]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Typography } from '@material-ui/core/'
|
||||
import QRCodeModal from './QRCodeModal'
|
||||
import ClipboardCopy from './ClipboardCopy'
|
||||
|
||||
import Identicon from 'react-identicons'
|
||||
import { ReactElement } from 'react'
|
||||
import Identicon from 'react-identicons'
|
||||
import { config } from '../config'
|
||||
import ClipboardCopy from './ClipboardCopy'
|
||||
import QRCodeModal from './QRCodeModal'
|
||||
|
||||
interface Props {
|
||||
address: string | undefined
|
||||
@@ -36,9 +36,7 @@ export default function EthereumAddress(props: Props): ReactElement {
|
||||
}
|
||||
: { marginRight: '7px' }
|
||||
}
|
||||
href={`${process.env.REACT_APP_BLOCKCHAIN_EXPLORER_URL}/${props.transaction ? 'tx' : 'address'}/${
|
||||
props.address
|
||||
}`}
|
||||
href={`${config.BLOCKCHAIN_EXPLORER_URL}/${props.transaction ? 'tx' : 'address'}/${props.address}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
|
||||
@@ -55,6 +55,7 @@ interface Props {
|
||||
onChange?: (value: string) => void
|
||||
onConfirm: (value: string) => void
|
||||
mapperFn?: (value: string) => string
|
||||
locked?: boolean
|
||||
}
|
||||
|
||||
export default function ExpandableListItemKey({
|
||||
@@ -68,6 +69,7 @@ export default function ExpandableListItemKey({
|
||||
helperText,
|
||||
placeholder,
|
||||
mapperFn,
|
||||
locked,
|
||||
}: Props): ReactElement | null {
|
||||
const classes = useStyles()
|
||||
const [open, setOpen] = useState(Boolean(expandedOnly))
|
||||
@@ -96,7 +98,7 @@ export default function ExpandableListItemKey({
|
||||
<Typography variant="body2">
|
||||
<div>
|
||||
{!open && value}
|
||||
{!expandedOnly && (
|
||||
{!expandedOnly && !locked && (
|
||||
<IconButton size="small" className={classes.copyValue}>
|
||||
{open ? (
|
||||
<Minus onClick={toggleOpen} strokeWidth={1} />
|
||||
@@ -116,6 +118,7 @@ export default function ExpandableListItemKey({
|
||||
fullWidth
|
||||
className={classes.content}
|
||||
autoFocus
|
||||
hidden={locked}
|
||||
/>
|
||||
</Collapse>
|
||||
</Grid>
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { ReactElement } from 'react'
|
||||
import { BookOpen, DollarSign, FileText, Home, Layers, Settings } from 'react-feather'
|
||||
import { Link } from 'react-router-dom'
|
||||
import Logo from '../assets/logo.svg'
|
||||
import { config } from '../config'
|
||||
import { ROUTES } from '../routes'
|
||||
import SideBarItem from './SideBarItem'
|
||||
import SideBarStatus from './SideBarStatus'
|
||||
@@ -111,7 +112,7 @@ export default function SideBar(): ReactElement {
|
||||
</List>
|
||||
<Divider className={classes.divider} />
|
||||
<List>
|
||||
<MUILink href={process.env.REACT_APP_BEE_DOCS_HOST} target="_blank" className={classes.link}>
|
||||
<MUILink href={config.BEE_DOCS_HOST} target="_blank" className={classes.link}>
|
||||
<SideBarItem
|
||||
iconStart={<BookOpen className={classes.icon} />}
|
||||
iconEnd={<OpenInNewSharp className={classes.iconSmall} />}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { ReactElement } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import { Button, Grid, Link as MuiLink, Typography } from '@material-ui/core/'
|
||||
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
|
||||
import { Button, Grid, Typography, Link as MuiLink } from '@material-ui/core/'
|
||||
import { ROUTES } from '../routes'
|
||||
import type { ReactElement } from 'react'
|
||||
import { Activity } from 'react-feather'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { config } from '../config'
|
||||
import { ROUTES } from '../routes'
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -37,11 +37,11 @@ export default function TroubleshootConnectionCard(): ReactElement {
|
||||
<Grid item className={classes.content}>
|
||||
<Typography align="center">
|
||||
Please check your node status to fix the problem. You can also check out the{' '}
|
||||
<MuiLink href={process.env.REACT_APP_BEE_DOCS_HOST} target="_blank" rel="noreferrer">
|
||||
<MuiLink href={config.BEE_DOCS_HOST} target="_blank" rel="noreferrer">
|
||||
Swarm Bee Docs
|
||||
</MuiLink>{' '}
|
||||
or ask for support on the{' '}
|
||||
<MuiLink href={process.env.REACT_APP_BEE_DISCORD_HOST} target="_blank" rel="noreferrer">
|
||||
<MuiLink href={config.BEE_DISCORD_HOST} target="_blank" rel="noreferrer">
|
||||
Ethereum Swarm Discord
|
||||
</MuiLink>
|
||||
.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
function getProcessEnv(key: string): string | undefined | false {
|
||||
return typeof process === 'object' && process.env[key]
|
||||
}
|
||||
|
||||
class Config {
|
||||
public readonly BEE_API_HOST: string
|
||||
public readonly BEE_DEBUG_API_HOST: string
|
||||
public readonly BLOCKCHAIN_EXPLORER_URL: string
|
||||
public readonly BEE_DOCS_HOST: string
|
||||
public readonly BEE_DISCORD_HOST: string
|
||||
public readonly GITHUB_REPO_URL: string
|
||||
|
||||
constructor() {
|
||||
this.BEE_API_HOST =
|
||||
sessionStorage.getItem('api_host') || getProcessEnv('REACT_APP_BEE_HOST') || 'http://localhost:1633'
|
||||
this.BEE_DEBUG_API_HOST =
|
||||
sessionStorage.getItem('debug_api_host') || getProcessEnv('REACT_APP_BEE_DEBUG_HOST') || 'http://localhost:1635'
|
||||
this.BLOCKCHAIN_EXPLORER_URL =
|
||||
getProcessEnv('REACT_APP_BLOCKCHAIN_EXPLORER_URL') || 'https://blockscout.com/xdai/mainnet'
|
||||
this.BEE_DOCS_HOST = getProcessEnv('REACT_APP_BEE_DOCS_HOST') || 'https://docs.ethswarm.org/docs/'
|
||||
this.BEE_DISCORD_HOST = getProcessEnv('REACT_APP_BEE_DISCORD_HOST') || 'https://discord.gg/eKr9XPv7'
|
||||
this.GITHUB_REPO_URL =
|
||||
getProcessEnv('REACT_APP_BEE_GITHUB_REPO_URL') || 'https://api.github.com/repos/ethersphere/bee'
|
||||
}
|
||||
}
|
||||
|
||||
export const config = new Config()
|
||||
|
||||
export default config
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import axios from 'axios'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { config } from '../config'
|
||||
|
||||
export interface LatestBeeReleaseHook {
|
||||
latestBeeRelease: LatestBeeRelease | null
|
||||
@@ -14,7 +15,7 @@ export const useLatestBeeRelease = (): LatestBeeReleaseHook => {
|
||||
|
||||
useEffect(() => {
|
||||
axios
|
||||
.get(`${process.env.REACT_APP_BEE_GITHUB_REPO_URL}/releases/latest`)
|
||||
.get(`${config.GITHUB_REPO_URL}/releases/latest`)
|
||||
.then(res => {
|
||||
setLatestBeeRelease(res.data)
|
||||
})
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import ExpandableList from '../../components/ExpandableList'
|
||||
import ExpandableListItemInput from '../../components/ExpandableListItemInput'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
|
||||
export default function Settings(): ReactElement {
|
||||
const { apiUrl, apiDebugUrl, setApiUrl, setDebugApiUrl } = useContext(SettingsContext)
|
||||
const { apiUrl, apiDebugUrl, setApiUrl, setDebugApiUrl, lockedApiSettings } = useContext(SettingsContext)
|
||||
|
||||
return (
|
||||
<ExpandableList label="API Settings" defaultOpen>
|
||||
<ExpandableListItemInput label="Bee API" value={apiUrl} onConfirm={setApiUrl} />
|
||||
<ExpandableListItemInput label="Bee Debug API" value={apiDebugUrl} onConfirm={setDebugApiUrl} />
|
||||
<ExpandableListItemInput label="Bee API" value={apiUrl} onConfirm={setApiUrl} locked={lockedApiSettings} />
|
||||
<ExpandableListItemInput
|
||||
label="Bee Debug API"
|
||||
value={apiDebugUrl}
|
||||
onConfirm={setDebugApiUrl}
|
||||
locked={lockedApiSettings}
|
||||
/>
|
||||
</ExpandableList>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createContext, ReactChild, ReactElement, useState, useEffect } from 'react'
|
||||
import { Bee, BeeDebug } from '@ethersphere/bee-js'
|
||||
import { createContext, ReactChild, ReactElement, useEffect, useState } from 'react'
|
||||
import { config } from '../config'
|
||||
|
||||
interface ContextInterface {
|
||||
apiUrl: string
|
||||
@@ -8,16 +9,17 @@ interface ContextInterface {
|
||||
beeDebugApi: BeeDebug | null
|
||||
setApiUrl: (url: string) => void
|
||||
setDebugApiUrl: (url: string) => void
|
||||
lockedApiSettings: boolean
|
||||
}
|
||||
|
||||
const initialValues: ContextInterface = {
|
||||
apiUrl: sessionStorage.getItem('api_host') || process.env.REACT_APP_BEE_HOST || 'http://localhost:1633',
|
||||
apiDebugUrl:
|
||||
sessionStorage.getItem('debug_api_host') || process.env.REACT_APP_BEE_DEBUG_HOST || 'http://localhost:1635',
|
||||
apiUrl: config.BEE_API_HOST,
|
||||
apiDebugUrl: config.BEE_DEBUG_API_HOST,
|
||||
beeApi: null,
|
||||
beeDebugApi: null,
|
||||
setApiUrl: () => {}, // eslint-disable-line
|
||||
setDebugApiUrl: () => {}, // eslint-disable-line
|
||||
lockedApiSettings: false,
|
||||
}
|
||||
|
||||
export const Context = createContext<ContextInterface>(initialValues)
|
||||
@@ -25,13 +27,22 @@ export const Consumer = Context.Consumer
|
||||
|
||||
interface Props {
|
||||
children: ReactChild
|
||||
beeApiUrl?: string
|
||||
beeDebugApiUrl?: string
|
||||
lockedApiSettings?: boolean
|
||||
}
|
||||
|
||||
export function Provider({ children }: Props): ReactElement {
|
||||
export function Provider({
|
||||
children,
|
||||
beeApiUrl,
|
||||
beeDebugApiUrl,
|
||||
lockedApiSettings: extLockedApiSettings,
|
||||
}: Props): ReactElement {
|
||||
const [apiUrl, setApiUrl] = useState<string>(initialValues.apiUrl)
|
||||
const [apiDebugUrl, setDebugApiUrl] = useState<string>(initialValues.apiDebugUrl)
|
||||
const [beeApi, setBeeApi] = useState<Bee | null>(null)
|
||||
const [beeDebugApi, setBeeDebugApi] = useState<BeeDebug | null>(null)
|
||||
const [lockedApiSettings] = useState<boolean>(Boolean(extLockedApiSettings))
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
@@ -42,6 +53,14 @@ export function Provider({ children }: Props): ReactElement {
|
||||
}
|
||||
}, [apiUrl])
|
||||
|
||||
useEffect(() => {
|
||||
if (beeApiUrl) setApiUrl(beeApiUrl)
|
||||
}, [beeApiUrl])
|
||||
|
||||
useEffect(() => {
|
||||
if (beeDebugApiUrl) setDebugApiUrl(beeDebugApiUrl)
|
||||
}, [beeDebugApiUrl])
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
setBeeDebugApi(new BeeDebug(apiDebugUrl))
|
||||
@@ -52,7 +71,9 @@ export function Provider({ children }: Props): ReactElement {
|
||||
}, [apiDebugUrl])
|
||||
|
||||
return (
|
||||
<Context.Provider value={{ apiUrl, apiDebugUrl, beeApi, beeDebugApi, setApiUrl, setDebugApiUrl }}>
|
||||
<Context.Provider
|
||||
value={{ apiUrl, apiDebugUrl, beeApi, beeDebugApi, setApiUrl, setDebugApiUrl, lockedApiSettings }}
|
||||
>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
)
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import { detectIndexHtml } from './file'
|
||||
|
||||
describe('file utils', () => {
|
||||
it('detectIndexHtml should find index.html', () => {
|
||||
expect(
|
||||
detectIndexHtml([
|
||||
{ name: 'swarm.png', path: 'swarm.png' },
|
||||
{ name: 'index.html', path: 'index.html' },
|
||||
]),
|
||||
).toBe('index.html')
|
||||
})
|
||||
|
||||
it('detectIndexHtml should find index.htm', () => {
|
||||
expect(
|
||||
detectIndexHtml([
|
||||
{ name: 'index.htm', path: 'index.htm' },
|
||||
{ name: 'swarm.png', path: 'swarm.png' },
|
||||
]),
|
||||
).toBe('index.htm')
|
||||
})
|
||||
|
||||
it('detectIndexHtml should find nested index.html', () => {
|
||||
expect(
|
||||
detectIndexHtml([
|
||||
{ name: 'swarm.png', path: 'sample-folder/swarm.png' },
|
||||
{ name: 'index.html', path: 'sample-folder/index.html' },
|
||||
]),
|
||||
).toBe('index.html')
|
||||
})
|
||||
|
||||
it('detectIndexHtml should not find nested index.htm when ambigous', () => {
|
||||
expect(
|
||||
detectIndexHtml([
|
||||
{ name: 'index.htm', path: 'sample-folder/index.htm' },
|
||||
{ name: 'swarm.png', path: 'other-folder/swarm.png' },
|
||||
]),
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('detectIndexHtml should not find deep index.html', () => {
|
||||
expect(
|
||||
detectIndexHtml([
|
||||
{ name: 'index.html', path: 'sample-folder/index.html' },
|
||||
{ name: 'swarm.png', path: 'swarm.png' },
|
||||
]),
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('detectIndexHtml should return false when no matches appear', () => {
|
||||
expect(
|
||||
detectIndexHtml([
|
||||
{ name: 'swarm.png', path: 'swarm.png' },
|
||||
{ name: 'swarm.jpg', path: 'swarm.jpg' },
|
||||
]),
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,106 +0,0 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { extractSwarmHash, isInteger, makeBigNumber } from './index'
|
||||
|
||||
describe('utils', () => {
|
||||
describe('isInteger', () => {
|
||||
const correctValues = [
|
||||
BigInt(0),
|
||||
BigInt(1),
|
||||
BigInt(-1),
|
||||
new BigNumber('1'),
|
||||
new BigNumber('0'),
|
||||
new BigNumber('-1'),
|
||||
]
|
||||
const wrongValues = ['1', new BigNumber('-0.1'), new BigNumber(NaN), new BigNumber(Infinity)]
|
||||
|
||||
correctValues.forEach(v => {
|
||||
test(`testing ${v}`, () => {
|
||||
expect(isInteger(v)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
wrongValues.forEach(v => {
|
||||
test(`testing ${v}`, () => {
|
||||
expect(isInteger(v)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('makeBigNumber', () => {
|
||||
const correctValues = [
|
||||
BigInt(0),
|
||||
BigInt(1),
|
||||
BigInt(-1),
|
||||
'1',
|
||||
'0',
|
||||
'-1',
|
||||
'0.1',
|
||||
new BigNumber('1'),
|
||||
new BigNumber('0'),
|
||||
new BigNumber('-1'),
|
||||
0,
|
||||
1,
|
||||
-1,
|
||||
]
|
||||
const wrongValues = [new Function()] // eslint-disable-line no-new-func
|
||||
|
||||
correctValues.forEach(v => {
|
||||
test(`testing ${v}`, () => {
|
||||
expect(BigNumber.isBigNumber(makeBigNumber(v))).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
wrongValues.forEach(v => {
|
||||
test(`testing ${v}`, () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
expect(() => makeBigNumber(v as unknown as any)).toThrow()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('extractSwarmHash', () => {
|
||||
test('should return 64 hash', () => {
|
||||
expect(extractSwarmHash('7f0fe712cdd78bdea52d040369eb32b6af5ecd01fa5ae49b7506412abdd81ac3')).toBe(
|
||||
'7f0fe712cdd78bdea52d040369eb32b6af5ecd01fa5ae49b7506412abdd81ac3',
|
||||
)
|
||||
})
|
||||
|
||||
test('should return 128 hash', () => {
|
||||
expect(
|
||||
extractSwarmHash(
|
||||
'd1829242c4d08e9f914fedfb1f68aacd62826d75370a9a57a80c9e6e6a49983c767c013be9aa4319e34fd8323ef0f2a57426b30e66c87e219f6f6359e2595e7f',
|
||||
),
|
||||
).toBe(
|
||||
'd1829242c4d08e9f914fedfb1f68aacd62826d75370a9a57a80c9e6e6a49983c767c013be9aa4319e34fd8323ef0f2a57426b30e66c87e219f6f6359e2595e7f',
|
||||
)
|
||||
})
|
||||
|
||||
test('should return 64 hash from url', () => {
|
||||
expect(
|
||||
extractSwarmHash('http://localhost:1633/bzz/7f0fe712cdd78bdea52d040369eb32b6af5ecd01fa5ae49b7506412abdd81ac3/'),
|
||||
).toBe('7f0fe712cdd78bdea52d040369eb32b6af5ecd01fa5ae49b7506412abdd81ac3')
|
||||
})
|
||||
|
||||
test('should return 128 hash from url', () => {
|
||||
expect(
|
||||
extractSwarmHash(
|
||||
'http://localhost:1633/bzz/d1829242c4d08e9f914fedfb1f68aacd62826d75370a9a57a80c9e6e6a49983c767c013be9aa4319e34fd8323ef0f2a57426b30e66c87e219f6f6359e2595e7f/',
|
||||
),
|
||||
).toBe(
|
||||
'd1829242c4d08e9f914fedfb1f68aacd62826d75370a9a57a80c9e6e6a49983c767c013be9aa4319e34fd8323ef0f2a57426b30e66c87e219f6f6359e2595e7f',
|
||||
)
|
||||
})
|
||||
|
||||
test('should return null when nothing is found', () => {
|
||||
expect(extractSwarmHash('Bee Dashboard')).toBe(null)
|
||||
})
|
||||
|
||||
test('should return null when length is incorrect', () => {
|
||||
expect(extractSwarmHash('7f0fe712cdd78bdea52d040369eb32b6af5ecd01fa5ae49b7506412abdd81a')).toBe(null)
|
||||
})
|
||||
|
||||
test('should return null when alphanumeric', () => {
|
||||
expect(extractSwarmHash('gkQ6duo5iHJ099g908P0t17ZWFf8Ke2klrywLP5BGtLkcaEC5W0kLEfbe4wUnDI6')).toBe(null)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
const OPTIMAL_CONNECTED_PEERS = 200
|
||||
const OPTIMAL_POPULATION = 100_000
|
||||
const OPTIMAL_POPULATION = 100000
|
||||
const OPTIMAL_DEPTH = 12
|
||||
|
||||
interface Threshold {
|
||||
|
||||
Reference in New Issue
Block a user